From 87ae9b4b74f42d4a4b38b610b185171d612c01b0 Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Tue, 31 Oct 2023 17:15:06 -0700 Subject: [PATCH 01/14] Fix #4011: Handle more huggable immediately nested parens/brackets. --- CHANGES.md | 4 +- docs/the_black_code_style/future_style.md | 17 ++++- src/black/linegen.py | 15 +++-- ..._parens_with_braces_and_square_brackets.py | 66 ++++++++++++++++--- .../cases/preview_long_strings__regression.py | 15 ++--- 5 files changed, 90 insertions(+), 27 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e910fbed162..04ae89827d4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,8 +13,8 @@ ### Preview style -- Multiline dictionaries and lists that are the sole argument to a function are now - indented less (#3964) +- Multiline tuples, lists, and dictionaries that are the sole argument to a function + or inside another tuple, list, or dictionary are now indented less (#3964, #4012) - Multiline list and dict unpacking as the sole argument to a function is now also indented less (#3992) diff --git a/docs/the_black_code_style/future_style.md b/docs/the_black_code_style/future_style.md index 944ffad033e..428bd87ab50 100644 --- a/docs/the_black_code_style/future_style.md +++ b/docs/the_black_code_style/future_style.md @@ -116,8 +116,7 @@ my_dict = { ### Improved multiline dictionary and list indentation for sole function parameter For better readability and less verticality, _Black_ now pairs parentheses ("(", ")") -with braces ("{", "}") and square brackets ("[", "]") on the same line for single -parameter function calls. For example: +with braces ("{", "}") and square brackets ("[", "]") on the same line. For example: ```python foo( @@ -127,6 +126,14 @@ foo( 3, ] ) + +nested_array = [ + [ + 1, + 2, + 3, + ] +] ``` will be changed to: @@ -137,6 +144,12 @@ foo([ 2, 3, ]) + +nested_array = [[ + 1, + 2, + 3, +]] ``` This also applies to list and dictionary unpacking: diff --git a/src/black/linegen.py b/src/black/linegen.py index 121c6e314fe..92bbef5d75c 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -817,18 +817,21 @@ def _first_right_hand_split( body_leaves.reverse() head_leaves.reverse() - if Preview.hug_parens_with_braces_and_square_brackets in line.mode: + if ( + Preview.hug_parens_with_braces_and_square_brackets in line.mode + and tail_leaves[0].value + and tail_leaves[0].opening_bracket is head_leaves[-1] + ): is_unpacking = 1 if body_leaves[0].type in [token.STAR, token.DOUBLESTAR] else 0 - if ( - tail_leaves[0].type == token.RPAR - and tail_leaves[0].value - and tail_leaves[0].opening_bracket is head_leaves[-1] - and body_leaves[-1].type in [token.RBRACE, token.RSQB] + while ( + len(body_leaves) > 2 + is_unpacking + and body_leaves[-1].type in CLOSING_BRACKETS and body_leaves[-1].opening_bracket is body_leaves[is_unpacking] ): head_leaves = head_leaves + body_leaves[: 1 + is_unpacking] tail_leaves = body_leaves[-1:] + tail_leaves body_leaves = body_leaves[1 + is_unpacking : -1] + is_unpacking = 0 head = bracket_split_build_line( head_leaves, line, opening_bracket, component=_BracketSplitComponent.head diff --git a/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py b/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py index 51fe516add5..2ddcaa52860 100644 --- a/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py +++ b/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py @@ -128,6 +128,9 @@ def foo_square_brackets(request): func({"short line"}) func({"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"}) func({{"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"}}) +func(("long line", "long long line", "long long long line", "long long long long line", "long long long long long line")) +func((("long line", "long long line", "long long long line", "long long long long line", "long long long long long line"))) +func([["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]) foooooooooooooooooooo( [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} @@ -137,6 +140,10 @@ def foo_square_brackets(request): [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ) +nested_mapping = {"key": [{"a very long key 1": "with a very long value", "a very long key 2": "with a very long value"}]} +nested_array = [[["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]] +explicit_exploding = [[["short", "line",],],] + foo(*["long long long long long line", "long long long long long line", "long long long long long line"]) foo(*[str(i) for i in range(100000000000000000000000000000000000000000000000000000000000)]) @@ -285,15 +292,34 @@ def foo_square_brackets(request): "long long long long line", "long long long long long line", }) -func({ - { - "long line", - "long long line", - "long long long line", - "long long long long line", - "long long long long long line", - } -}) +func({{ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +}}) +func(( + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +)) +func((( + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +))) +func([[ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +]]) foooooooooooooooooooo( [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} @@ -303,6 +329,28 @@ def foo_square_brackets(request): [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ) +nested_mapping = { + "key": [{ + "a very long key 1": "with a very long value", + "a very long key 2": "with a very long value", + }] +} +nested_array = [[[ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +]]] +explicit_exploding = [ + [ + [ + "short", + "line", + ], + ], +] + foo(*[ "long long long long long line", "long long long long long line", diff --git a/tests/data/cases/preview_long_strings__regression.py b/tests/data/cases/preview_long_strings__regression.py index 313d898cd83..5e76a8cf61c 100644 --- a/tests/data/cases/preview_long_strings__regression.py +++ b/tests/data/cases/preview_long_strings__regression.py @@ -611,14 +611,13 @@ def foo(): class A: def foo(): - XXXXXXXXXXXX.append( - ( - "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})" - .format(xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx), - my_var, - my_other_var, - ) - ) + XXXXXXXXXXXX.append(( + "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})".format( + xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx + ), + my_var, + my_other_var, + )) class A: From d55b39f6ab2315cf5f74c852a2936c81ab1ccf9b Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Tue, 31 Oct 2023 17:18:25 -0700 Subject: [PATCH 02/14] Make this comparison more clear, though it doesn't matter in practice. --- src/black/linegen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 92bbef5d75c..a1bd11357fb 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -824,7 +824,7 @@ def _first_right_hand_split( ): is_unpacking = 1 if body_leaves[0].type in [token.STAR, token.DOUBLESTAR] else 0 while ( - len(body_leaves) > 2 + is_unpacking + len(body_leaves) >= 2 + is_unpacking and body_leaves[-1].type in CLOSING_BRACKETS and body_leaves[-1].opening_bracket is body_leaves[is_unpacking] ): From 09aad71104a93d5225611538eb7766675710044e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 00:19:31 +0000 Subject: [PATCH 03/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- CHANGES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 04ae89827d4..69b4b58c2e5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,8 +13,8 @@ ### Preview style -- Multiline tuples, lists, and dictionaries that are the sole argument to a function - or inside another tuple, list, or dictionary are now indented less (#3964, #4012) +- Multiline tuples, lists, and dictionaries that are the sole argument to a function or + inside another tuple, list, or dictionary are now indented less (#3964, #4012) - Multiline list and dict unpacking as the sole argument to a function is now also indented less (#3992) From 80cd4697777cdfd204e244cbf69cd2cc1117875b Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Tue, 31 Oct 2023 17:21:52 -0700 Subject: [PATCH 04/14] Format black code. --- src/black/handle_ipynb_magics.py | 54 ++++++++++++++------------------ src/black/lines.py | 6 ++-- src/blackd/middlewares.py | 6 ++-- src/blib2to3/pgen2/tokenize.py | 6 ++-- 4 files changed, 33 insertions(+), 39 deletions(-) diff --git a/src/black/handle_ipynb_magics.py b/src/black/handle_ipynb_magics.py index 55ef2267df8..5b2847cb0c4 100644 --- a/src/black/handle_ipynb_magics.py +++ b/src/black/handle_ipynb_magics.py @@ -17,36 +17,30 @@ from black.output import out from black.report import NothingChanged -TRANSFORMED_MAGICS = frozenset( - ( - "get_ipython().run_cell_magic", - "get_ipython().system", - "get_ipython().getoutput", - "get_ipython().run_line_magic", - ) -) -TOKENS_TO_IGNORE = frozenset( - ( - "ENDMARKER", - "NL", - "NEWLINE", - "COMMENT", - "DEDENT", - "UNIMPORTANT_WS", - "ESCAPED_NL", - ) -) -PYTHON_CELL_MAGICS = frozenset( - ( - "capture", - "prun", - "pypy", - "python", - "python3", - "time", - "timeit", - ) -) +TRANSFORMED_MAGICS = frozenset(( + "get_ipython().run_cell_magic", + "get_ipython().system", + "get_ipython().getoutput", + "get_ipython().run_line_magic", +)) +TOKENS_TO_IGNORE = frozenset(( + "ENDMARKER", + "NL", + "NEWLINE", + "COMMENT", + "DEDENT", + "UNIMPORTANT_WS", + "ESCAPED_NL", +)) +PYTHON_CELL_MAGICS = frozenset(( + "capture", + "prun", + "pypy", + "python", + "python3", + "time", + "timeit", +)) TOKEN_HEX = secrets.token_hex diff --git a/src/black/lines.py b/src/black/lines.py index a73c429e3d9..fc864cdb2f6 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -200,9 +200,9 @@ def is_triple_quoted_string(self) -> bool: value = self.leaves[0].value if value.startswith(('"""', "'''")): return True - if Preview.accept_raw_docstrings in self.mode and value.startswith( - ("r'''", 'r"""', "R'''", 'R"""') - ): + if Preview.accept_raw_docstrings in self.mode and value.startswith(( + "r'''", 'r"""', "R'''", 'R"""' + )): return True return False diff --git a/src/blackd/middlewares.py b/src/blackd/middlewares.py index 370e0ae222e..0b9fc0264fc 100644 --- a/src/blackd/middlewares.py +++ b/src/blackd/middlewares.py @@ -36,9 +36,9 @@ async def impl(request: Request, handler: Handler) -> StreamResponse: resp.headers["Access-Control-Expose-Headers"] = "*" if is_options: resp.headers["Access-Control-Allow-Headers"] = ", ".join(allow_headers) - resp.headers["Access-Control-Allow-Methods"] = ", ".join( - ("OPTIONS", "POST") - ) + resp.headers["Access-Control-Allow-Methods"] = ", ".join(( + "OPTIONS", "POST" + )) return resp diff --git a/src/blib2to3/pgen2/tokenize.py b/src/blib2to3/pgen2/tokenize.py index d0607f4b1e1..011277abef9 100644 --- a/src/blib2to3/pgen2/tokenize.py +++ b/src/blib2to3/pgen2/tokenize.py @@ -314,9 +314,9 @@ def _get_normal_name(orig_enc: str) -> str: enc = orig_enc[:12].lower().replace("_", "-") if enc == "utf-8" or enc.startswith("utf-8-"): return "utf-8" - if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or enc.startswith( - ("latin-1-", "iso-8859-1-", "iso-latin-1-") - ): + if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or enc.startswith(( + "latin-1-", "iso-8859-1-", "iso-latin-1-" + )): return "iso-8859-1" return orig_enc From fa28b0deb4318d064a9e68434bfff3f82eb1d1f8 Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Tue, 31 Oct 2023 17:25:15 -0700 Subject: [PATCH 05/14] Format again. --- docs/conf.py | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 6b645435325..52a849d06a4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -149,15 +149,13 @@ def make_pypi_svg(version: str) -> None: # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). -latex_documents = [ - ( - master_doc, - "black.tex", - "Documentation for Black", - "Łukasz Langa and contributors to Black", - "manual", - ) -] +latex_documents = [( + master_doc, + "black.tex", + "Documentation for Black", + "Łukasz Langa and contributors to Black", + "manual", +)] # -- Options for manual page output ------------------------------------------ @@ -172,17 +170,15 @@ def make_pypi_svg(version: str) -> None: # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) -texinfo_documents = [ - ( - master_doc, - "Black", - "Documentation for Black", - author, - "Black", - "The uncompromising Python code formatter", - "Miscellaneous", - ) -] +texinfo_documents = [( + master_doc, + "Black", + "Documentation for Black", + author, + "Black", + "The uncompromising Python code formatter", + "Miscellaneous", +)] # -- Options for Epub output ------------------------------------------------- From c93d20f763a7451dce77383dc7111920eeb6ae47 Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Tue, 31 Oct 2023 20:44:50 -0700 Subject: [PATCH 06/14] Update CHANGES.md Co-authored-by: Jelle Zijlstra --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 3511b8266b9..730d302c9d1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,7 +14,7 @@ ### Preview style - Multiline tuples, lists, and dictionaries that are the sole argument to a function or - inside another tuple, list, or dictionary are now indented less (#3964, #4012) + inside another tuple, list, or dictionary are now indented less (#3964) (#4012) - Multiline list and dict unpacking as the sole argument to a function is now also indented less (#3992) From 1678c808dc323bab9747474c8f685ba32fe59ed3 Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Mon, 6 Nov 2023 20:00:42 -0800 Subject: [PATCH 07/14] Do not hug if the argument fits in a single line. --- src/black/cache.py | 6 +- src/black/linegen.py | 57 +++++++++++++++---- src/black/lines.py | 6 +- src/blackd/middlewares.py | 6 +- src/blib2to3/pgen2/tokenize.py | 6 +- ..._parens_with_braces_and_square_brackets.py | 30 +++++++++- 6 files changed, 85 insertions(+), 26 deletions(-) diff --git a/src/black/cache.py b/src/black/cache.py index 6a332304981..6baa096baca 100644 --- a/src/black/cache.py +++ b/src/black/cache.py @@ -124,9 +124,9 @@ def filtered_cached(self, sources: Iterable[Path]) -> Tuple[Set[Path], Set[Path] def write(self, sources: Iterable[Path]) -> None: """Update the cache file data and write a new cache file.""" - self.file_data.update(**{ - str(src.resolve()): Cache.get_file_data(src) for src in sources - }) + self.file_data.update( + **{str(src.resolve()): Cache.get_file_data(src) for src in sources} + ) try: CACHE_DIR.mkdir(parents=True, exist_ok=True) with tempfile.NamedTemporaryFile( diff --git a/src/black/linegen.py b/src/black/linegen.py index a1bd11357fb..49982b6a0fe 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -817,28 +817,63 @@ def _first_right_hand_split( body_leaves.reverse() head_leaves.reverse() + body: Optional[Line] = None if ( Preview.hug_parens_with_braces_and_square_brackets in line.mode and tail_leaves[0].value and tail_leaves[0].opening_bracket is head_leaves[-1] ): - is_unpacking = 1 if body_leaves[0].type in [token.STAR, token.DOUBLESTAR] else 0 + inner_body_leaves = list(body_leaves) + hugged_opening_leaves: List[Leaf] = [] + hugged_closing_leaves: List[Leaf] = [] + is_unpacking = body_leaves[0].type in [token.STAR, token.DOUBLESTAR] + unpacking_offset: int = 1 if is_unpacking else 0 while ( - len(body_leaves) >= 2 + is_unpacking - and body_leaves[-1].type in CLOSING_BRACKETS - and body_leaves[-1].opening_bracket is body_leaves[is_unpacking] + len(inner_body_leaves) >= 2 + unpacking_offset + and inner_body_leaves[-1].type in CLOSING_BRACKETS + and inner_body_leaves[-1].opening_bracket + is inner_body_leaves[unpacking_offset] ): - head_leaves = head_leaves + body_leaves[: 1 + is_unpacking] - tail_leaves = body_leaves[-1:] + tail_leaves - body_leaves = body_leaves[1 + is_unpacking : -1] - is_unpacking = 0 + if unpacking_offset: + hugged_opening_leaves.append(inner_body_leaves.pop(0)) + unpacking_offset = 0 + hugged_opening_leaves.append(inner_body_leaves.pop(0)) + hugged_closing_leaves.insert(0, inner_body_leaves.pop()) + + if hugged_opening_leaves: + inner_body = bracket_split_build_line( + inner_body_leaves, + line, + opening_bracket, + component=_BracketSplitComponent.body, + ) + if inner_body.should_split_rhs: + should_hug = True + else: + line_length = line.mode.line_length - sum( + len(str(leaf)) + for leaf in hugged_opening_leaves + hugged_closing_leaves + ) + if is_line_short_enough( + inner_body, mode=replace(line.mode, line_length=line_length) + ): + # Do not hug if it fits in a single line. + should_hug = False + else: + should_hug = True + if should_hug: + body_leaves = inner_body_leaves + head_leaves.extend(hugged_opening_leaves) + tail_leaves = hugged_closing_leaves + tail_leaves + body = inner_body # No need to re-calculate the body again later. head = bracket_split_build_line( head_leaves, line, opening_bracket, component=_BracketSplitComponent.head ) - body = bracket_split_build_line( - body_leaves, line, opening_bracket, component=_BracketSplitComponent.body - ) + if body is None: + body = bracket_split_build_line( + body_leaves, line, opening_bracket, component=_BracketSplitComponent.body + ) tail = bracket_split_build_line( tail_leaves, line, opening_bracket, component=_BracketSplitComponent.tail ) diff --git a/src/black/lines.py b/src/black/lines.py index fc864cdb2f6..a73c429e3d9 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -200,9 +200,9 @@ def is_triple_quoted_string(self) -> bool: value = self.leaves[0].value if value.startswith(('"""', "'''")): return True - if Preview.accept_raw_docstrings in self.mode and value.startswith(( - "r'''", 'r"""', "R'''", 'R"""' - )): + if Preview.accept_raw_docstrings in self.mode and value.startswith( + ("r'''", 'r"""', "R'''", 'R"""') + ): return True return False diff --git a/src/blackd/middlewares.py b/src/blackd/middlewares.py index 0b9fc0264fc..370e0ae222e 100644 --- a/src/blackd/middlewares.py +++ b/src/blackd/middlewares.py @@ -36,9 +36,9 @@ async def impl(request: Request, handler: Handler) -> StreamResponse: resp.headers["Access-Control-Expose-Headers"] = "*" if is_options: resp.headers["Access-Control-Allow-Headers"] = ", ".join(allow_headers) - resp.headers["Access-Control-Allow-Methods"] = ", ".join(( - "OPTIONS", "POST" - )) + resp.headers["Access-Control-Allow-Methods"] = ", ".join( + ("OPTIONS", "POST") + ) return resp diff --git a/src/blib2to3/pgen2/tokenize.py b/src/blib2to3/pgen2/tokenize.py index 011277abef9..d0607f4b1e1 100644 --- a/src/blib2to3/pgen2/tokenize.py +++ b/src/blib2to3/pgen2/tokenize.py @@ -314,9 +314,9 @@ def _get_normal_name(orig_enc: str) -> str: enc = orig_enc[:12].lower().replace("_", "-") if enc == "utf-8" or enc.startswith("utf-8-"): return "utf-8" - if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or enc.startswith(( - "latin-1-", "iso-8859-1-", "iso-latin-1-" - )): + if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or enc.startswith( + ("latin-1-", "iso-8859-1-", "iso-latin-1-") + ): return "iso-8859-1" return orig_enc diff --git a/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py b/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py index 2ddcaa52860..e7d9fdc2c2a 100644 --- a/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py +++ b/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py @@ -132,6 +132,13 @@ def foo_square_brackets(request): func((("long line", "long long line", "long long long line", "long long long long line", "long long long long long line"))) func([["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]) +# Do not hug if the argument fits in a single line. +func({"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}) +func(("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")) +func(["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]) +func(**{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"}) +func(*("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----")) + foooooooooooooooooooo( [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} ) @@ -273,9 +280,9 @@ def foo_square_brackets(request): ) func([x for x in "short line"]) -func([ - x for x in "long line long line long line long line long line long line long line" -]) +func( + [x for x in "long line long line long line long line long line long line long line"] +) func([ x for x in [ @@ -321,6 +328,23 @@ def foo_square_brackets(request): "long long long long long line", ]]) +# Do not hug if the argument fits in a single line. +func( + {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} +) +func( + ("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line") +) +func( + ["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"] +) +func( + **{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"} +) +func( + *("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----") +) + foooooooooooooooooooo( [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} ) From b51cef3544a4515f3979be75c0ff00b32af33d5b Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Tue, 7 Nov 2023 08:22:11 -0800 Subject: [PATCH 08/14] Use magic_trailing_comma instead of should_split_rhs for the shortcut. --- src/black/linegen.py | 9 +++++--- ..._parens_with_braces_and_square_brackets.py | 22 +++++++++++++++++-- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 49982b6a0fe..4c8a96ed2a1 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -844,10 +844,13 @@ def _first_right_hand_split( inner_body = bracket_split_build_line( inner_body_leaves, line, - opening_bracket, + hugged_opening_leaves[-1], component=_BracketSplitComponent.body, ) - if inner_body.should_split_rhs: + if ( + line.mode.magic_trailing_comma + and inner_body_leaves[-1].type == token.COMMA + ): should_hug = True else: line_length = line.mode.line_length - sum( @@ -857,7 +860,7 @@ def _first_right_hand_split( if is_line_short_enough( inner_body, mode=replace(line.mode, line_length=line_length) ): - # Do not hug if it fits in a single line. + # Do not hug if it fits on a single line. should_hug = False else: should_hug = True diff --git a/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py b/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py index e7d9fdc2c2a..bb65af824de 100644 --- a/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py +++ b/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py @@ -132,12 +132,15 @@ def foo_square_brackets(request): func((("long line", "long long line", "long long long line", "long long long long line", "long long long long long line"))) func([["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]) -# Do not hug if the argument fits in a single line. +# Do not hug if the argument fits on a single line. func({"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}) func(("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")) func(["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]) func(**{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"}) func(*("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----")) +array = [{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}] +array = [("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")] +array = [["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]] foooooooooooooooooooo( [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} @@ -150,6 +153,9 @@ def foo_square_brackets(request): nested_mapping = {"key": [{"a very long key 1": "with a very long value", "a very long key 2": "with a very long value"}]} nested_array = [[["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]] explicit_exploding = [[["short", "line",],],] +single_item_do_not_explode = Context({ + "version": get_docs_version(), +}) foo(*["long long long long long line", "long long long long long line", "long long long long long line"]) @@ -328,7 +334,7 @@ def foo_square_brackets(request): "long long long long long line", ]]) -# Do not hug if the argument fits in a single line. +# Do not hug if the argument fits on a single line. func( {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} ) @@ -344,6 +350,15 @@ def foo_square_brackets(request): func( *("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----") ) +array = [ + {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} +] +array = [ + ("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line") +] +array = [ + ["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"] +] foooooooooooooooooooo( [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} @@ -374,6 +389,9 @@ def foo_square_brackets(request): ], ], ] +single_item_do_not_explode = Context({ + "version": get_docs_version(), +}) foo(*[ "long long long long long line", From e3df49807456239750d909e3265c309b3aaf6c1b Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Tue, 7 Nov 2023 09:52:52 -0800 Subject: [PATCH 09/14] Fix crash. --- src/black/linegen.py | 2 +- .../preview_hug_parens_with_braces_and_square_brackets.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 1d8f9f71723..5158d90db49 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -840,7 +840,7 @@ def _first_right_hand_split( hugged_opening_leaves.append(inner_body_leaves.pop(0)) hugged_closing_leaves.insert(0, inner_body_leaves.pop()) - if hugged_opening_leaves: + if hugged_opening_leaves and inner_body_leaves: inner_body = bracket_split_build_line( inner_body_leaves, line, diff --git a/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py b/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py index bb65af824de..ede380b64f2 100644 --- a/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py +++ b/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py @@ -172,6 +172,9 @@ def foo_square_brackets(request): foo(**{x: y for x, y in enumerate(["long long long long line","long long long long line"])}) +# Edge case when deciding whether to hug the brackets without inner content. +very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName([[]]) + # output def foo_brackets(request): return JsonResponse({ @@ -413,3 +416,8 @@ def foo_square_brackets(request): foo(**{ x: y for x, y in enumerate(["long long long long line", "long long long long line"]) }) + +# Edge case when deciding whether to hug the brackets without inner content. +very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName( + [[]] +) From 36d954cee9f37c7fc56cd519eb615ae9e15d0aec Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Tue, 7 Nov 2023 10:47:45 -0800 Subject: [PATCH 10/14] empty commit to re-trigger CI From b0d01b2a0e4f62ce25bf7516521dbdf6eaf9e812 Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Tue, 7 Nov 2023 12:10:23 -0800 Subject: [PATCH 11/14] empty commit to re-trigger CI From e9fb7aed6ca1a0ac7a8f97f22f6c47260bc94360 Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Tue, 7 Nov 2023 17:40:37 -0800 Subject: [PATCH 12/14] empty commit to re-trigger CI From 6134969a23efb1f75594a3e26ba1993eb17c1ab4 Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Wed, 8 Nov 2023 19:47:07 -0800 Subject: [PATCH 13/14] Revert almost all changes to try if it's my PR causing diff-shades issues. --- docs/conf.py | 36 +++--- docs/the_black_code_style/future_style.md | 17 +-- src/black/cache.py | 6 +- src/black/handle_ipynb_magics.py | 54 ++++---- src/black/linegen.py | 70 +++------- ..._parens_with_braces_and_square_brackets.py | 122 ++---------------- .../cases/preview_long_strings__regression.py | 15 ++- 7 files changed, 90 insertions(+), 230 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 52a849d06a4..6b645435325 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -149,13 +149,15 @@ def make_pypi_svg(version: str) -> None: # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). -latex_documents = [( - master_doc, - "black.tex", - "Documentation for Black", - "Łukasz Langa and contributors to Black", - "manual", -)] +latex_documents = [ + ( + master_doc, + "black.tex", + "Documentation for Black", + "Łukasz Langa and contributors to Black", + "manual", + ) +] # -- Options for manual page output ------------------------------------------ @@ -170,15 +172,17 @@ def make_pypi_svg(version: str) -> None: # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) -texinfo_documents = [( - master_doc, - "Black", - "Documentation for Black", - author, - "Black", - "The uncompromising Python code formatter", - "Miscellaneous", -)] +texinfo_documents = [ + ( + master_doc, + "Black", + "Documentation for Black", + author, + "Black", + "The uncompromising Python code formatter", + "Miscellaneous", + ) +] # -- Options for Epub output ------------------------------------------------- diff --git a/docs/the_black_code_style/future_style.md b/docs/the_black_code_style/future_style.md index 428bd87ab50..944ffad033e 100644 --- a/docs/the_black_code_style/future_style.md +++ b/docs/the_black_code_style/future_style.md @@ -116,7 +116,8 @@ my_dict = { ### Improved multiline dictionary and list indentation for sole function parameter For better readability and less verticality, _Black_ now pairs parentheses ("(", ")") -with braces ("{", "}") and square brackets ("[", "]") on the same line. For example: +with braces ("{", "}") and square brackets ("[", "]") on the same line for single +parameter function calls. For example: ```python foo( @@ -126,14 +127,6 @@ foo( 3, ] ) - -nested_array = [ - [ - 1, - 2, - 3, - ] -] ``` will be changed to: @@ -144,12 +137,6 @@ foo([ 2, 3, ]) - -nested_array = [[ - 1, - 2, - 3, -]] ``` This also applies to list and dictionary unpacking: diff --git a/src/black/cache.py b/src/black/cache.py index 6baa096baca..6a332304981 100644 --- a/src/black/cache.py +++ b/src/black/cache.py @@ -124,9 +124,9 @@ def filtered_cached(self, sources: Iterable[Path]) -> Tuple[Set[Path], Set[Path] def write(self, sources: Iterable[Path]) -> None: """Update the cache file data and write a new cache file.""" - self.file_data.update( - **{str(src.resolve()): Cache.get_file_data(src) for src in sources} - ) + self.file_data.update(**{ + str(src.resolve()): Cache.get_file_data(src) for src in sources + }) try: CACHE_DIR.mkdir(parents=True, exist_ok=True) with tempfile.NamedTemporaryFile( diff --git a/src/black/handle_ipynb_magics.py b/src/black/handle_ipynb_magics.py index 5b2847cb0c4..55ef2267df8 100644 --- a/src/black/handle_ipynb_magics.py +++ b/src/black/handle_ipynb_magics.py @@ -17,30 +17,36 @@ from black.output import out from black.report import NothingChanged -TRANSFORMED_MAGICS = frozenset(( - "get_ipython().run_cell_magic", - "get_ipython().system", - "get_ipython().getoutput", - "get_ipython().run_line_magic", -)) -TOKENS_TO_IGNORE = frozenset(( - "ENDMARKER", - "NL", - "NEWLINE", - "COMMENT", - "DEDENT", - "UNIMPORTANT_WS", - "ESCAPED_NL", -)) -PYTHON_CELL_MAGICS = frozenset(( - "capture", - "prun", - "pypy", - "python", - "python3", - "time", - "timeit", -)) +TRANSFORMED_MAGICS = frozenset( + ( + "get_ipython().run_cell_magic", + "get_ipython().system", + "get_ipython().getoutput", + "get_ipython().run_line_magic", + ) +) +TOKENS_TO_IGNORE = frozenset( + ( + "ENDMARKER", + "NL", + "NEWLINE", + "COMMENT", + "DEDENT", + "UNIMPORTANT_WS", + "ESCAPED_NL", + ) +) +PYTHON_CELL_MAGICS = frozenset( + ( + "capture", + "prun", + "pypy", + "python", + "python3", + "time", + "timeit", + ) +) TOKEN_HEX = secrets.token_hex diff --git a/src/black/linegen.py b/src/black/linegen.py index 8a2cd4710b9..ed71785ea84 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -817,66 +817,26 @@ def _first_right_hand_split( body_leaves.reverse() head_leaves.reverse() - body: Optional[Line] = None - if ( - Preview.hug_parens_with_braces_and_square_brackets in line.mode - and tail_leaves[0].value - and tail_leaves[0].opening_bracket is head_leaves[-1] - ): - inner_body_leaves = list(body_leaves) - hugged_opening_leaves: List[Leaf] = [] - hugged_closing_leaves: List[Leaf] = [] - is_unpacking = body_leaves[0].type in [token.STAR, token.DOUBLESTAR] - unpacking_offset: int = 1 if is_unpacking else 0 - while ( - len(inner_body_leaves) >= 2 + unpacking_offset - and inner_body_leaves[-1].type in CLOSING_BRACKETS - and inner_body_leaves[-1].opening_bracket - is inner_body_leaves[unpacking_offset] + # TODO: Revert me back. + if Preview.hug_parens_with_braces_and_square_brackets in line.mode: + is_unpacking = 1 if body_leaves[0].type in [token.STAR, token.DOUBLESTAR] else 0 + if ( + tail_leaves[0].type == token.RPAR + and tail_leaves[0].value + and tail_leaves[0].opening_bracket is head_leaves[-1] + and body_leaves[-1].type in [token.RBRACE, token.RSQB] + and body_leaves[-1].opening_bracket is body_leaves[is_unpacking] ): - if unpacking_offset: - hugged_opening_leaves.append(inner_body_leaves.pop(0)) - unpacking_offset = 0 - hugged_opening_leaves.append(inner_body_leaves.pop(0)) - hugged_closing_leaves.insert(0, inner_body_leaves.pop()) - - if hugged_opening_leaves and inner_body_leaves: - inner_body = bracket_split_build_line( - inner_body_leaves, - line, - hugged_opening_leaves[-1], - component=_BracketSplitComponent.body, - ) - if ( - line.mode.magic_trailing_comma - and inner_body_leaves[-1].type == token.COMMA - ): - should_hug = True - else: - line_length = line.mode.line_length - sum( - len(str(leaf)) - for leaf in hugged_opening_leaves + hugged_closing_leaves - ) - if is_line_short_enough( - inner_body, mode=replace(line.mode, line_length=line_length) - ): - # Do not hug if it fits on a single line. - should_hug = False - else: - should_hug = True - if should_hug: - body_leaves = inner_body_leaves - head_leaves.extend(hugged_opening_leaves) - tail_leaves = hugged_closing_leaves + tail_leaves - body = inner_body # No need to re-calculate the body again later. + head_leaves = head_leaves + body_leaves[: 1 + is_unpacking] + tail_leaves = body_leaves[-1:] + tail_leaves + body_leaves = body_leaves[1 + is_unpacking : -1] head = bracket_split_build_line( head_leaves, line, opening_bracket, component=_BracketSplitComponent.head ) - if body is None: - body = bracket_split_build_line( - body_leaves, line, opening_bracket, component=_BracketSplitComponent.body - ) + body = bracket_split_build_line( + body_leaves, line, opening_bracket, component=_BracketSplitComponent.body + ) tail = bracket_split_build_line( tail_leaves, line, opening_bracket, component=_BracketSplitComponent.tail ) diff --git a/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py b/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py index 9e5c9eb8546..97b5b2e8dd1 100644 --- a/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py +++ b/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py @@ -128,19 +128,6 @@ def foo_square_brackets(request): func({"short line"}) func({"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"}) func({{"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"}}) -func(("long line", "long long line", "long long long line", "long long long long line", "long long long long long line")) -func((("long line", "long long line", "long long long line", "long long long long line", "long long long long long line"))) -func([["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]) - -# Do not hug if the argument fits on a single line. -func({"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}) -func(("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")) -func(["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]) -func(**{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"}) -func(*("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----")) -array = [{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}] -array = [("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")] -array = [["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]] foooooooooooooooooooo( [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} @@ -150,13 +137,6 @@ def foo_square_brackets(request): [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ) -nested_mapping = {"key": [{"a very long key 1": "with a very long value", "a very long key 2": "with a very long value"}]} -nested_array = [[["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]] -explicit_exploding = [[["short", "line",],],] -single_item_do_not_explode = Context({ - "version": get_docs_version(), -}) - foo(*["long long long long long line", "long long long long long line", "long long long long long line"]) foo(*[str(i) for i in range(100000000000000000000000000000000000000000000000000000000000)]) @@ -172,9 +152,6 @@ def foo_square_brackets(request): foo(**{x: y for x, y in enumerate(["long long long long line","long long long long line"])}) -# Edge case when deciding whether to hug the brackets without inner content. -very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName([[]]) - for foo in ["a", "b"]: output.extend([ individual @@ -299,9 +276,9 @@ def foo_square_brackets(request): ) func([x for x in "short line"]) -func( - [x for x in "long line long line long line long line long line long line long line"] -) +func([ + x for x in "long line long line long line long line long line long line long line" +]) func([ x for x in [ @@ -318,60 +295,15 @@ def foo_square_brackets(request): "long long long long line", "long long long long long line", }) -func({{ - "long line", - "long long line", - "long long long line", - "long long long long line", - "long long long long long line", -}}) -func(( - "long line", - "long long line", - "long long long line", - "long long long long line", - "long long long long long line", -)) -func((( - "long line", - "long long line", - "long long long line", - "long long long long line", - "long long long long long line", -))) -func([[ - "long line", - "long long line", - "long long long line", - "long long long long line", - "long long long long long line", -]]) - -# Do not hug if the argument fits on a single line. -func( - {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} -) -func( - ("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line") -) -func( - ["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"] -) -func( - **{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"} -) -func( - *("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----") -) -array = [ - {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} -] -array = [ - ("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line") -] -array = [ - ["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"] -] +func({ + { + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", + } +}) foooooooooooooooooooo( [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} @@ -381,31 +313,6 @@ def foo_square_brackets(request): [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ) -nested_mapping = { - "key": [{ - "a very long key 1": "with a very long value", - "a very long key 2": "with a very long value", - }] -} -nested_array = [[[ - "long line", - "long long line", - "long long long line", - "long long long long line", - "long long long long long line", -]]] -explicit_exploding = [ - [ - [ - "short", - "line", - ], - ], -] -single_item_do_not_explode = Context({ - "version": get_docs_version(), -}) - foo(*[ "long long long long long line", "long long long long long line", @@ -427,11 +334,6 @@ def foo_square_brackets(request): x: y for x, y in enumerate(["long long long long line", "long long long long line"]) }) -# Edge case when deciding whether to hug the brackets without inner content. -very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName( - [[]] -) - for foo in ["a", "b"]: output.extend([ individual diff --git a/tests/data/cases/preview_long_strings__regression.py b/tests/data/cases/preview_long_strings__regression.py index 5e76a8cf61c..313d898cd83 100644 --- a/tests/data/cases/preview_long_strings__regression.py +++ b/tests/data/cases/preview_long_strings__regression.py @@ -611,13 +611,14 @@ def foo(): class A: def foo(): - XXXXXXXXXXXX.append(( - "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})".format( - xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx - ), - my_var, - my_other_var, - )) + XXXXXXXXXXXX.append( + ( + "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})" + .format(xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx), + my_var, + my_other_var, + ) + ) class A: From 240c21816529fadba10202db5abed50049b57577 Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Wed, 8 Nov 2023 20:50:47 -0800 Subject: [PATCH 14/14] Revert "Revert almost all changes to try if it's my PR causing diff-shades issues." This reverts commit 6134969a23efb1f75594a3e26ba1993eb17c1ab4. --- docs/conf.py | 36 +++--- docs/the_black_code_style/future_style.md | 17 ++- src/black/cache.py | 6 +- src/black/handle_ipynb_magics.py | 54 ++++---- src/black/linegen.py | 70 +++++++--- ..._parens_with_braces_and_square_brackets.py | 122 ++++++++++++++++-- .../cases/preview_long_strings__regression.py | 15 +-- 7 files changed, 230 insertions(+), 90 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 6b645435325..52a849d06a4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -149,15 +149,13 @@ def make_pypi_svg(version: str) -> None: # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). -latex_documents = [ - ( - master_doc, - "black.tex", - "Documentation for Black", - "Łukasz Langa and contributors to Black", - "manual", - ) -] +latex_documents = [( + master_doc, + "black.tex", + "Documentation for Black", + "Łukasz Langa and contributors to Black", + "manual", +)] # -- Options for manual page output ------------------------------------------ @@ -172,17 +170,15 @@ def make_pypi_svg(version: str) -> None: # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) -texinfo_documents = [ - ( - master_doc, - "Black", - "Documentation for Black", - author, - "Black", - "The uncompromising Python code formatter", - "Miscellaneous", - ) -] +texinfo_documents = [( + master_doc, + "Black", + "Documentation for Black", + author, + "Black", + "The uncompromising Python code formatter", + "Miscellaneous", +)] # -- Options for Epub output ------------------------------------------------- diff --git a/docs/the_black_code_style/future_style.md b/docs/the_black_code_style/future_style.md index 944ffad033e..428bd87ab50 100644 --- a/docs/the_black_code_style/future_style.md +++ b/docs/the_black_code_style/future_style.md @@ -116,8 +116,7 @@ my_dict = { ### Improved multiline dictionary and list indentation for sole function parameter For better readability and less verticality, _Black_ now pairs parentheses ("(", ")") -with braces ("{", "}") and square brackets ("[", "]") on the same line for single -parameter function calls. For example: +with braces ("{", "}") and square brackets ("[", "]") on the same line. For example: ```python foo( @@ -127,6 +126,14 @@ foo( 3, ] ) + +nested_array = [ + [ + 1, + 2, + 3, + ] +] ``` will be changed to: @@ -137,6 +144,12 @@ foo([ 2, 3, ]) + +nested_array = [[ + 1, + 2, + 3, +]] ``` This also applies to list and dictionary unpacking: diff --git a/src/black/cache.py b/src/black/cache.py index 6a332304981..6baa096baca 100644 --- a/src/black/cache.py +++ b/src/black/cache.py @@ -124,9 +124,9 @@ def filtered_cached(self, sources: Iterable[Path]) -> Tuple[Set[Path], Set[Path] def write(self, sources: Iterable[Path]) -> None: """Update the cache file data and write a new cache file.""" - self.file_data.update(**{ - str(src.resolve()): Cache.get_file_data(src) for src in sources - }) + self.file_data.update( + **{str(src.resolve()): Cache.get_file_data(src) for src in sources} + ) try: CACHE_DIR.mkdir(parents=True, exist_ok=True) with tempfile.NamedTemporaryFile( diff --git a/src/black/handle_ipynb_magics.py b/src/black/handle_ipynb_magics.py index 55ef2267df8..5b2847cb0c4 100644 --- a/src/black/handle_ipynb_magics.py +++ b/src/black/handle_ipynb_magics.py @@ -17,36 +17,30 @@ from black.output import out from black.report import NothingChanged -TRANSFORMED_MAGICS = frozenset( - ( - "get_ipython().run_cell_magic", - "get_ipython().system", - "get_ipython().getoutput", - "get_ipython().run_line_magic", - ) -) -TOKENS_TO_IGNORE = frozenset( - ( - "ENDMARKER", - "NL", - "NEWLINE", - "COMMENT", - "DEDENT", - "UNIMPORTANT_WS", - "ESCAPED_NL", - ) -) -PYTHON_CELL_MAGICS = frozenset( - ( - "capture", - "prun", - "pypy", - "python", - "python3", - "time", - "timeit", - ) -) +TRANSFORMED_MAGICS = frozenset(( + "get_ipython().run_cell_magic", + "get_ipython().system", + "get_ipython().getoutput", + "get_ipython().run_line_magic", +)) +TOKENS_TO_IGNORE = frozenset(( + "ENDMARKER", + "NL", + "NEWLINE", + "COMMENT", + "DEDENT", + "UNIMPORTANT_WS", + "ESCAPED_NL", +)) +PYTHON_CELL_MAGICS = frozenset(( + "capture", + "prun", + "pypy", + "python", + "python3", + "time", + "timeit", +)) TOKEN_HEX = secrets.token_hex diff --git a/src/black/linegen.py b/src/black/linegen.py index ed71785ea84..8a2cd4710b9 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -817,26 +817,66 @@ def _first_right_hand_split( body_leaves.reverse() head_leaves.reverse() - # TODO: Revert me back. - if Preview.hug_parens_with_braces_and_square_brackets in line.mode: - is_unpacking = 1 if body_leaves[0].type in [token.STAR, token.DOUBLESTAR] else 0 - if ( - tail_leaves[0].type == token.RPAR - and tail_leaves[0].value - and tail_leaves[0].opening_bracket is head_leaves[-1] - and body_leaves[-1].type in [token.RBRACE, token.RSQB] - and body_leaves[-1].opening_bracket is body_leaves[is_unpacking] + body: Optional[Line] = None + if ( + Preview.hug_parens_with_braces_and_square_brackets in line.mode + and tail_leaves[0].value + and tail_leaves[0].opening_bracket is head_leaves[-1] + ): + inner_body_leaves = list(body_leaves) + hugged_opening_leaves: List[Leaf] = [] + hugged_closing_leaves: List[Leaf] = [] + is_unpacking = body_leaves[0].type in [token.STAR, token.DOUBLESTAR] + unpacking_offset: int = 1 if is_unpacking else 0 + while ( + len(inner_body_leaves) >= 2 + unpacking_offset + and inner_body_leaves[-1].type in CLOSING_BRACKETS + and inner_body_leaves[-1].opening_bracket + is inner_body_leaves[unpacking_offset] ): - head_leaves = head_leaves + body_leaves[: 1 + is_unpacking] - tail_leaves = body_leaves[-1:] + tail_leaves - body_leaves = body_leaves[1 + is_unpacking : -1] + if unpacking_offset: + hugged_opening_leaves.append(inner_body_leaves.pop(0)) + unpacking_offset = 0 + hugged_opening_leaves.append(inner_body_leaves.pop(0)) + hugged_closing_leaves.insert(0, inner_body_leaves.pop()) + + if hugged_opening_leaves and inner_body_leaves: + inner_body = bracket_split_build_line( + inner_body_leaves, + line, + hugged_opening_leaves[-1], + component=_BracketSplitComponent.body, + ) + if ( + line.mode.magic_trailing_comma + and inner_body_leaves[-1].type == token.COMMA + ): + should_hug = True + else: + line_length = line.mode.line_length - sum( + len(str(leaf)) + for leaf in hugged_opening_leaves + hugged_closing_leaves + ) + if is_line_short_enough( + inner_body, mode=replace(line.mode, line_length=line_length) + ): + # Do not hug if it fits on a single line. + should_hug = False + else: + should_hug = True + if should_hug: + body_leaves = inner_body_leaves + head_leaves.extend(hugged_opening_leaves) + tail_leaves = hugged_closing_leaves + tail_leaves + body = inner_body # No need to re-calculate the body again later. head = bracket_split_build_line( head_leaves, line, opening_bracket, component=_BracketSplitComponent.head ) - body = bracket_split_build_line( - body_leaves, line, opening_bracket, component=_BracketSplitComponent.body - ) + if body is None: + body = bracket_split_build_line( + body_leaves, line, opening_bracket, component=_BracketSplitComponent.body + ) tail = bracket_split_build_line( tail_leaves, line, opening_bracket, component=_BracketSplitComponent.tail ) diff --git a/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py b/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py index 97b5b2e8dd1..9e5c9eb8546 100644 --- a/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py +++ b/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py @@ -128,6 +128,19 @@ def foo_square_brackets(request): func({"short line"}) func({"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"}) func({{"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"}}) +func(("long line", "long long line", "long long long line", "long long long long line", "long long long long long line")) +func((("long line", "long long line", "long long long line", "long long long long line", "long long long long long line"))) +func([["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]) + +# Do not hug if the argument fits on a single line. +func({"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}) +func(("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")) +func(["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]) +func(**{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"}) +func(*("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----")) +array = [{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}] +array = [("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")] +array = [["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]] foooooooooooooooooooo( [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} @@ -137,6 +150,13 @@ def foo_square_brackets(request): [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ) +nested_mapping = {"key": [{"a very long key 1": "with a very long value", "a very long key 2": "with a very long value"}]} +nested_array = [[["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]] +explicit_exploding = [[["short", "line",],],] +single_item_do_not_explode = Context({ + "version": get_docs_version(), +}) + foo(*["long long long long long line", "long long long long long line", "long long long long long line"]) foo(*[str(i) for i in range(100000000000000000000000000000000000000000000000000000000000)]) @@ -152,6 +172,9 @@ def foo_square_brackets(request): foo(**{x: y for x, y in enumerate(["long long long long line","long long long long line"])}) +# Edge case when deciding whether to hug the brackets without inner content. +very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName([[]]) + for foo in ["a", "b"]: output.extend([ individual @@ -276,9 +299,9 @@ def foo_square_brackets(request): ) func([x for x in "short line"]) -func([ - x for x in "long line long line long line long line long line long line long line" -]) +func( + [x for x in "long line long line long line long line long line long line long line"] +) func([ x for x in [ @@ -295,15 +318,60 @@ def foo_square_brackets(request): "long long long long line", "long long long long long line", }) -func({ - { - "long line", - "long long line", - "long long long line", - "long long long long line", - "long long long long long line", - } -}) +func({{ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +}}) +func(( + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +)) +func((( + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +))) +func([[ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +]]) + +# Do not hug if the argument fits on a single line. +func( + {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} +) +func( + ("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line") +) +func( + ["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"] +) +func( + **{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"} +) +func( + *("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----") +) +array = [ + {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} +] +array = [ + ("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line") +] +array = [ + ["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"] +] foooooooooooooooooooo( [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} @@ -313,6 +381,31 @@ def foo_square_brackets(request): [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ) +nested_mapping = { + "key": [{ + "a very long key 1": "with a very long value", + "a very long key 2": "with a very long value", + }] +} +nested_array = [[[ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +]]] +explicit_exploding = [ + [ + [ + "short", + "line", + ], + ], +] +single_item_do_not_explode = Context({ + "version": get_docs_version(), +}) + foo(*[ "long long long long long line", "long long long long long line", @@ -334,6 +427,11 @@ def foo_square_brackets(request): x: y for x, y in enumerate(["long long long long line", "long long long long line"]) }) +# Edge case when deciding whether to hug the brackets without inner content. +very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName( + [[]] +) + for foo in ["a", "b"]: output.extend([ individual diff --git a/tests/data/cases/preview_long_strings__regression.py b/tests/data/cases/preview_long_strings__regression.py index 313d898cd83..5e76a8cf61c 100644 --- a/tests/data/cases/preview_long_strings__regression.py +++ b/tests/data/cases/preview_long_strings__regression.py @@ -611,14 +611,13 @@ def foo(): class A: def foo(): - XXXXXXXXXXXX.append( - ( - "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})" - .format(xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx), - my_var, - my_other_var, - ) - ) + XXXXXXXXXXXX.append(( + "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})".format( + xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx + ), + my_var, + my_other_var, + )) class A: