node.insert_child(index, Node(syms.atom, [lpar, operand, rpar]))
yield from self.visit_default(node)
+ def visit_tname(self, node: Node) -> Iterator[Line]:
+ """
+ Add potential parentheses around types in function parameter lists to be made
+ into real parentheses in case the type hint is too long to fit on a line
+ Examples:
+ def foo(a: int, b: float = 7): ...
+
+ ->
+
+ def foo(a: (int), b: (float) = 7): ...
+ """
+ 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)
+
def visit_STRING(self, leaf: Leaf) -> Iterator[Line]:
if Preview.hex_codes_in_unicode_sequences in self.mode:
normalize_unicode_escape_sequences(leaf)
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"})
Returns whether the node should itself be wrapped in invisible parentheses.
"""
if (
- node.type != syms.atom
+ node.type not in (syms.atom, syms.expr)
or is_empty_tuple(node)
or is_one_tuple(node)
or (is_yield(node) and parent.type != syms.expr_stmt)
syms.except_clause,
syms.funcdef,
syms.with_stmt,
+ syms.tname,
# these ones aren't useful to end users, but they do please fuzzers
syms.for_stmt,
syms.del_stmt,
--- /dev/null
+# This has always worked
+z= Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong
+
+# "AnnAssign"s now also work
+z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong
+z: (Short
+ | Short2
+ | Short3
+ | Short4)
+z: (int)
+z: ((int))
+
+
+z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong = 7
+z: (Short
+ | Short2
+ | Short3
+ | Short4) = 8
+z: (int) = 2.3
+z: ((int)) = foo()
+
+# In case I go for not enforcing parantheses, this might get improved at the same time
+x = (
+ z
+ == 9999999999999999999999999999999999999999
+ | 9999999999999999999999999999999999999999
+ | 9999999999999999999999999999999999999999
+ | 9999999999999999999999999999999999999999,
+ y
+ == 9999999999999999999999999999999999999999
+ + 9999999999999999999999999999999999999999
+ + 9999999999999999999999999999999999999999
+ + 9999999999999999999999999999999999999999,
+)
+
+x = (
+ z == (9999999999999999999999999999999999999999
+ | 9999999999999999999999999999999999999999
+ | 9999999999999999999999999999999999999999
+ | 9999999999999999999999999999999999999999),
+ y == (9999999999999999999999999999999999999999
+ + 9999999999999999999999999999999999999999
+ + 9999999999999999999999999999999999999999
+ + 9999999999999999999999999999999999999999),
+)
+
+# handle formatting of "tname"s in parameter list
+
+# remove unnecessary paren
+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,
+ x: Loooooooooooooooooooooooong
+ | Looooooooooooooooong
+ | Looooooooooooooooooooong
+ | Looooooong,
+ *,
+ s: str,
+) -> None:
+ pass
+
+
+@app.get("/path/")
+async def foo(
+ q: str
+ | None = Query(None, title="Some long title", description="Some long description")
+):
+ pass
+
+
+def f(
+ max_jobs: int
+ | None = Option(
+ None, help="Maximum number of jobs to launch. And some additional text."
+ ),
+ another_option: bool = False
+ ):
+ ...
+
+
+# output
+# This has always worked
+z = (
+ Loooooooooooooooooooooooong
+ | Loooooooooooooooooooooooong
+ | Loooooooooooooooooooooooong
+ | Loooooooooooooooooooooooong
+)
+
+# "AnnAssign"s now also work
+z: (
+ Loooooooooooooooooooooooong
+ | Loooooooooooooooooooooooong
+ | Loooooooooooooooooooooooong
+ | Loooooooooooooooooooooooong
+)
+z: Short | Short2 | Short3 | Short4
+z: int
+z: int
+
+
+z: (
+ Loooooooooooooooooooooooong
+ | Loooooooooooooooooooooooong
+ | Loooooooooooooooooooooooong
+ | Loooooooooooooooooooooooong
+) = 7
+z: Short | Short2 | Short3 | Short4 = 8
+z: int = 2.3
+z: int = foo()
+
+# In case I go for not enforcing parantheses, this might get improved at the same time
+x = (
+ z
+ == 9999999999999999999999999999999999999999
+ | 9999999999999999999999999999999999999999
+ | 9999999999999999999999999999999999999999
+ | 9999999999999999999999999999999999999999,
+ y
+ == 9999999999999999999999999999999999999999
+ + 9999999999999999999999999999999999999999
+ + 9999999999999999999999999999999999999999
+ + 9999999999999999999999999999999999999999,
+)
+
+x = (
+ z
+ == (
+ 9999999999999999999999999999999999999999
+ | 9999999999999999999999999999999999999999
+ | 9999999999999999999999999999999999999999
+ | 9999999999999999999999999999999999999999
+ ),
+ y
+ == (
+ 9999999999999999999999999999999999999999
+ + 9999999999999999999999999999999999999999
+ + 9999999999999999999999999999999999999999
+ + 9999999999999999999999999999999999999999
+ ),
+)
+
+# handle formatting of "tname"s in parameter list
+
+
+# remove unnecessary paren
+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,
+ x: (
+ Loooooooooooooooooooooooong
+ | Looooooooooooooooong
+ | Looooooooooooooooooooong
+ | Looooooong
+ ),
+ *,
+ s: str,
+) -> None:
+ pass
+
+
+@app.get("/path/")
+async def foo(
+ q: str | None = Query(
+ None, title="Some long title", description="Some long description"
+ )
+):
+ pass
+
+
+def f(
+ max_jobs: int | None = Option(
+ None, help="Maximum number of jobs to launch. And some additional text."
+ ),
+ another_option: bool = False,
+): ...