From fd311b96c61fef2dd984bab5df337807b8aff8b2 Mon Sep 17 00:00:00 2001 From: "Terence D. Honles" Date: Thu, 22 Aug 2024 08:20:41 +0200 Subject: [PATCH] fix: fix PEP 646 support of tuple unpacking This change fixes unpacking a tuple or generic type when *args is a type variable tuple. --- CHANGES.md | 3 +++ docs/the_black_code_style/future_style.md | 2 ++ src/black/mode.py | 1 + src/black/nodes.py | 10 ++++++++-- src/black/resources/black.schema.json | 3 ++- .../preview_pep646_typed_star_arg_type_var_tuple.py | 8 ++++++++ tests/test_black.py | 3 +++ 7 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 tests/data/cases/preview_pep646_typed_star_arg_type_var_tuple.py diff --git a/CHANGES.md b/CHANGES.md index 23ffb7cf4e0..af12df0211a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,9 @@ +- Fix type annotation spacing between * and more complex type variable tuple (i.e. `def + fn(*args: *tuple[*Ts, T]) -> None: pass`) (#4440) + ### Configuration diff --git a/docs/the_black_code_style/future_style.md b/docs/the_black_code_style/future_style.md index e17c52fd4b2..cd4fb12bd51 100644 --- a/docs/the_black_code_style/future_style.md +++ b/docs/the_black_code_style/future_style.md @@ -36,6 +36,8 @@ Currently, the following features are included in the preview style: `case` blocks. - `parens_for_long_if_clauses_in_case_block`: Adds parentheses to `if` clauses in `case` blocks when the line is too long +- `pep646_typed_star_arg_type_var_tuple`: fix type annotation spacing between * and more + complex type variable tuple (i.e. `def fn(*args: *tuple[*Ts, T]) -> None: pass`) (labels/unstable-features)= diff --git a/src/black/mode.py b/src/black/mode.py index ca224fc8da1..43c60049cba 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -206,6 +206,7 @@ class Preview(Enum): docstring_check_for_newline = auto() remove_redundant_guard_parens = auto() parens_for_long_if_clauses_in_case_block = auto() + pep646_typed_star_arg_type_var_tuple = auto() UNSTABLE_FEATURES: Set[Preview] = { diff --git a/src/black/nodes.py b/src/black/nodes.py index 9579b715ad2..dae787939ea 100644 --- a/src/black/nodes.py +++ b/src/black/nodes.py @@ -254,9 +254,15 @@ def whitespace(leaf: Leaf, *, complex_subscript: bool, mode: Mode) -> str: # no elif ( prevp.type == token.STAR and parent_type(prevp) == syms.star_expr - and parent_type(prevp.parent) == syms.subscriptlist + and ( + parent_type(prevp.parent) == syms.subscriptlist + or ( + Preview.pep646_typed_star_arg_type_var_tuple in mode + and parent_type(prevp.parent) == syms.tname_star + ) + ) ): - # No space between typevar tuples. + # No space between typevar tuples or unpacking them. return NO elif prevp.type in VARARGS_SPECIALS: diff --git a/src/black/resources/black.schema.json b/src/black/resources/black.schema.json index 66f33c83902..f5c80dd4353 100644 --- a/src/black/resources/black.schema.json +++ b/src/black/resources/black.schema.json @@ -90,7 +90,8 @@ "is_simple_lookup_for_doublestar_expression", "docstring_check_for_newline", "remove_redundant_guard_parens", - "parens_for_long_if_clauses_in_case_block" + "parens_for_long_if_clauses_in_case_block", + "pep646_typed_star_arg_type_var_tuple" ] }, "description": "Enable specific features included in the `--unstable` style. Requires `--preview`. No compatibility guarantees are provided on the behavior or existence of any unstable features." diff --git a/tests/data/cases/preview_pep646_typed_star_arg_type_var_tuple.py b/tests/data/cases/preview_pep646_typed_star_arg_type_var_tuple.py new file mode 100644 index 00000000000..fb79e9983b1 --- /dev/null +++ b/tests/data/cases/preview_pep646_typed_star_arg_type_var_tuple.py @@ -0,0 +1,8 @@ +# flags: --minimum-version=3.11 --preview + + +def fn(*args: *tuple[*A, B]) -> None: + pass + + +fn.__annotations__ diff --git a/tests/test_black.py b/tests/test_black.py index 34699b80663..6fd725208d2 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -906,6 +906,9 @@ def test_get_features_used(self) -> None: self.check_features_used("a[*b]", {Feature.VARIADIC_GENERICS}) self.check_features_used("a[x, *y(), z] = t", {Feature.VARIADIC_GENERICS}) self.check_features_used("def fn(*args: *T): pass", {Feature.VARIADIC_GENERICS}) + self.check_features_used( + "def fn(*args: *tuple[*T]): pass", {Feature.VARIADIC_GENERICS} + ) self.check_features_used("with a: pass", set()) self.check_features_used("with a, b: pass", set())