diff --git a/CHANGES.md b/CHANGES.md index a68106ad23f..a879ab3e8da 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,9 @@ ### Preview style +- Long type hints are now wrapped in parentheses and properly indented when split across + multiple lines (#3899) + ### Configuration diff --git a/src/black/linegen.py b/src/black/linegen.py index 17735c8d833..9ddd4619f69 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -408,9 +408,10 @@ def foo(a: int, b: float = 7): ... def foo(a: (int), b: (float) = 7): ... """ - assert len(node.children) == 3 - if maybe_make_parens_invisible_in_atom(node.children[2], parent=node): - wrap_in_parentheses(node, node.children[2], visible=False) + if Preview.parenthesize_long_type_hints in self.mode: + assert len(node.children) == 3 + if maybe_make_parens_invisible_in_atom(node.children[2], parent=node): + wrap_in_parentheses(node, node.children[2], visible=False) yield from self.visit_default(node) @@ -515,7 +516,14 @@ def __post_init__(self) -> None: self.visit_except_clause = partial(v, keywords={"except"}, parens={"except"}) self.visit_with_stmt = partial(v, keywords={"with"}, parens={"with"}) self.visit_classdef = partial(v, keywords={"class"}, parens=Ø) - self.visit_expr_stmt = partial(v, keywords=Ø, parens=ASSIGNMENTS) + + # When this is moved out of preview, add ":" directly to ASSIGNMENTS in nodes.py + if Preview.parenthesize_long_type_hints in self.mode: + assignments = ASSIGNMENTS | {":"} + else: + assignments = ASSIGNMENTS + self.visit_expr_stmt = partial(v, keywords=Ø, parens=assignments) + self.visit_return_stmt = partial(v, keywords={"return"}, parens={"return"}) self.visit_import_from = partial(v, keywords=Ø, parens={"import"}) self.visit_del_stmt = partial(v, keywords=Ø, parens={"del"}) diff --git a/src/black/mode.py b/src/black/mode.py index 8a855ac495a..f44a821bcd0 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -180,6 +180,7 @@ class Preview(Enum): # for https://github.com/psf/black/issues/3117 to be fixed. string_processing = auto() parenthesize_conditional_expressions = auto() + parenthesize_long_type_hints = auto() skip_magic_trailing_comma_in_subscript = auto() wrap_long_dict_values_in_parens = auto() wrap_multiple_context_managers_in_parens = auto() diff --git a/src/black/nodes.py b/src/black/nodes.py index 06ef37829cb..edd201a21e9 100644 --- a/src/black/nodes.py +++ b/src/black/nodes.py @@ -121,8 +121,6 @@ ">>=", "**=", "//=", - # also handle annassign - ":", } IMPLICIT_TUPLE: Final = {syms.testlist, syms.testlist_star_expr, syms.exprlist} diff --git a/tests/data/miscellaneous/long_strings_flag_disabled.py b/tests/data/miscellaneous/long_strings_flag_disabled.py index f8792b3ef5a..db3954e3abd 100644 --- a/tests/data/miscellaneous/long_strings_flag_disabled.py +++ b/tests/data/miscellaneous/long_strings_flag_disabled.py @@ -254,9 +254,7 @@ + CONCATENATED + "using the '+' operator." ) -annotated_variable: ( - Final -) = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." annotated_variable: Literal[ "fakse_literal" ] = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." diff --git a/tests/data/py_310/pep604_union_types_line_breaks.py b/tests/data/preview_py_310/pep604_union_types_line_breaks.py similarity index 98% rename from tests/data/py_310/pep604_union_types_line_breaks.py rename to tests/data/preview_py_310/pep604_union_types_line_breaks.py index cdfbeb1d154..9c4ab870766 100644 --- a/tests/data/py_310/pep604_union_types_line_breaks.py +++ b/tests/data/preview_py_310/pep604_union_types_line_breaks.py @@ -149,13 +149,11 @@ def f( # remove unnecessary paren -def foo(i: int) -> None: - ... +def foo(i: int) -> None: ... # this is a syntax error in the type annotation according to mypy, but it's not invalid *python* code, so make sure we don't mess with it and make it so. -def foo(i: (int,)) -> None: - ... +def foo(i: (int,)) -> None: ... def foo( @@ -186,5 +184,4 @@ def f( None, help="Maximum number of jobs to launch. And some additional text." ), another_option: bool = False, -): - ... +): ...