From be336bb67fb6c12667836f7fba4993f9be9c61dd Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 20 Nov 2023 22:33:16 -0800 Subject: [PATCH 01/12] Run lint job on Ubuntu only (#4061) --- .github/workflows/lint.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d1ad23b..9c7aca8 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,11 +11,7 @@ jobs: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macOS-latest, windows-latest] + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From fb5e5d2be6367a0402a60da94f139dfb8943ed37 Mon Sep 17 00:00:00 2001 From: Henri Holopainen Date: Thu, 23 Nov 2023 05:11:49 +0200 Subject: [PATCH 02/12] Prefer more equal signs before a break when splitting chained assignments (#4010) Fixes #4007 --- CHANGES.md | 2 +- src/black/linegen.py | 64 ++++++++++++++------ src/black/lines.py | 5 ++ tests/data/cases/preview_prefer_rhs_split.py | 21 +++++++ 4 files changed, 71 insertions(+), 21 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 18bab51..6a8b97c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,8 +12,8 @@ ### Preview style +- Prefer more equal signs before a break when splitting chained assignments (#4010) - Standalone form feed characters at the module level are no longer removed (#4021) - - Additional cases of immediately nested tuples, lists, and dictionaries are now indented less (#4012) diff --git a/src/black/linegen.py b/src/black/linegen.py index 7fbbe29..7152568 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -910,24 +910,32 @@ def _maybe_split_omitting_optional_parens( try: # The RHSResult Omitting Optional Parens. rhs_oop = _first_right_hand_split(line, omit=omit) - if not ( + prefer_splitting_rhs_mode = ( Preview.prefer_splitting_right_hand_side_of_assignments in line.mode - # the split is right after `=` - and len(rhs.head.leaves) >= 2 - and rhs.head.leaves[-2].type == token.EQUAL - # the left side of assignment contains brackets - and any(leaf.type in BRACKETS for leaf in rhs.head.leaves[:-1]) - # the left side of assignment is short enough (the -1 is for the ending - # optional paren) - and is_line_short_enough( - rhs.head, mode=replace(mode, line_length=mode.line_length - 1) + ) + is_split_right_after_equal = ( + len(rhs.head.leaves) >= 2 and rhs.head.leaves[-2].type == token.EQUAL + ) + rhs_head_contains_brackets = any( + leaf.type in BRACKETS for leaf in rhs.head.leaves[:-1] + ) + # the -1 is for the ending optional paren + rhs_head_short_enough = is_line_short_enough( + rhs.head, mode=replace(mode, line_length=mode.line_length - 1) + ) + rhs_head_explode_blocked_by_magic_trailing_comma = ( + rhs.head.magic_trailing_comma is None + ) + if ( + not ( + prefer_splitting_rhs_mode + and is_split_right_after_equal + and rhs_head_contains_brackets + and rhs_head_short_enough + and rhs_head_explode_blocked_by_magic_trailing_comma ) - # the left side of assignment won't explode further because of magic - # trailing comma - and rhs.head.magic_trailing_comma is None - # the split by omitting optional parens isn't preferred by some other - # reason - and not _prefer_split_rhs_oop(rhs_oop, mode) + # the omit optional parens split is preferred by some other reason + or _prefer_split_rhs_oop_over_rhs(rhs_oop, rhs, mode) ): yield from _maybe_split_omitting_optional_parens( rhs_oop, line, mode, features=features, omit=omit @@ -935,8 +943,12 @@ def _maybe_split_omitting_optional_parens( return except CannotSplit as e: - if not ( - can_be_split(rhs.body) or is_line_short_enough(rhs.body, mode=mode) + # For chained assignments we want to use the previous successful split + if line.is_chained_assignment: + pass + + elif not can_be_split(rhs.body) and not is_line_short_enough( + rhs.body, mode=mode ): raise CannotSplit( "Splitting failed, body is still too long and can't be split." @@ -960,10 +972,22 @@ def _maybe_split_omitting_optional_parens( yield result -def _prefer_split_rhs_oop(rhs_oop: RHSResult, mode: Mode) -> bool: +def _prefer_split_rhs_oop_over_rhs( + rhs_oop: RHSResult, rhs: RHSResult, mode: Mode +) -> bool: """ - Returns whether we should prefer the result from a split omitting optional parens. + Returns whether we should prefer the result from a split omitting optional parens + (rhs_oop) over the original (rhs). """ + # If we have multiple targets, we prefer more `=`s on the head vs pushing them to + # the body + rhs_head_equal_count = [leaf.type for leaf in rhs.head.leaves].count(token.EQUAL) + rhs_oop_head_equal_count = [leaf.type for leaf in rhs_oop.head.leaves].count( + token.EQUAL + ) + if rhs_head_equal_count > 1 and rhs_head_equal_count > rhs_oop_head_equal_count: + return False + has_closing_bracket_after_assign = False for leaf in reversed(rhs_oop.head.leaves): if leaf.type == token.EQUAL: diff --git a/src/black/lines.py b/src/black/lines.py index ec6145f..6e33ee5 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -209,6 +209,11 @@ def is_triple_quoted_string(self) -> bool: return True return False + @property + def is_chained_assignment(self) -> bool: + """Is the line a chained assignment""" + return [leaf.type for leaf in self.leaves].count(token.EQUAL) > 1 + @property def opens_block(self) -> bool: """Does this line open a new level of indentation.""" diff --git a/tests/data/cases/preview_prefer_rhs_split.py b/tests/data/cases/preview_prefer_rhs_split.py index c732c33..28d89c3 100644 --- a/tests/data/cases/preview_prefer_rhs_split.py +++ b/tests/data/cases/preview_prefer_rhs_split.py @@ -84,3 +84,24 @@ ) or ( isinstance(some_other_var, BaseClass) and table.something != table.some_other_thing ) + +# Multiple targets +a = b = ( + ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) + +a = b = c = d = e = f = g = ( + hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +) = i = j = ( + kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +) + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = c + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = ( + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) = ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd From 69d49c5a6fe064eb290dd6445745fdeb2643f54f Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 24 Nov 2023 14:19:54 +0000 Subject: [PATCH 03/12] Bump mypy to 1.7.1 (#4069) --- .pre-commit-config.yaml | 2 +- CHANGES.md | 2 +- pyproject.toml | 4 ++-- src/blib2to3/pgen2/tokenize.py | 7 ++----- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c153746..2896489 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,7 +39,7 @@ repos: exclude: ^src/blib2to3/ - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.6.1 + rev: v1.7.1 hooks: - id: mypy exclude: ^docs/conf.py diff --git a/CHANGES.md b/CHANGES.md index 6a8b97c..f6fe69b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -26,7 +26,7 @@ ### Packaging -- Upgrade to mypy 1.6.1 (#4049) +- Upgrade to mypy 1.7.1 (#4049) (#4069) ### Parser diff --git a/pyproject.toml b/pyproject.toml index e63e0ae..6b681e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -121,7 +121,7 @@ macos-max-compat = true enable-by-default = false dependencies = [ "hatch-mypyc>=0.16.0", - "mypy==1.6.1", + "mypy==1.7.1", "click==8.1.3", # avoid https://github.com/pallets/click/issues/2558 ] require-runtime-dependencies = true @@ -187,7 +187,7 @@ CC = "clang" build-frontend = { name = "build", args = ["--no-isolation"] } # Unfortunately, hatch doesn't respect MACOSX_DEPLOYMENT_TARGET before-build = [ - "python -m pip install 'hatchling==1.18.0' hatch-vcs hatch-fancy-pypi-readme 'hatch-mypyc>=0.16.0' 'mypy==1.6.1' 'click==8.1.3'", + "python -m pip install 'hatchling==1.18.0' hatch-vcs hatch-fancy-pypi-readme 'hatch-mypyc>=0.16.0' 'mypy==1.7.1' 'click==8.1.3'", """sed -i '' -e "600,700s/'10_16'/os.environ['MACOSX_DEPLOYMENT_TARGET'].replace('.', '_')/" $(python -c 'import hatchling.builders.wheel as h; print(h.__file__)') """, ] diff --git a/src/blib2to3/pgen2/tokenize.py b/src/blib2to3/pgen2/tokenize.py index d0607f4..b04b18b 100644 --- a/src/blib2to3/pgen2/tokenize.py +++ b/src/blib2to3/pgen2/tokenize.py @@ -39,7 +39,6 @@ Set, Tuple, Union, - cast, ) from blib2to3.pgen2.grammar import Grammar @@ -262,11 +261,9 @@ def add_whitespace(self, start: Coord) -> None: def untokenize(self, iterable: Iterable[TokenInfo]) -> str: for t in iterable: if len(t) == 2: - self.compat(cast(Tuple[int, str], t), iterable) + self.compat(t, iterable) break - tok_type, token, start, end, line = cast( - Tuple[int, str, Coord, Coord, str], t - ) + tok_type, token, start, end, line = t self.add_whitespace(start) self.tokens.append(token) self.prev_row, self.prev_col = end From a0e270d0f246387202e676b25abbf7a02ddcbc71 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 24 Nov 2023 18:05:59 +0000 Subject: [PATCH 04/12] Build mypycified wheels for Python 3.12 (#4070) --- .github/workflows/pypi_upload.yml | 2 +- CHANGES.md | 1 + pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pypi_upload.yml b/.github/workflows/pypi_upload.yml index 07273f0..bbdcdf1 100644 --- a/.github/workflows/pypi_upload.yml +++ b/.github/workflows/pypi_upload.yml @@ -73,7 +73,7 @@ jobs: | pyp 'json.dumps({"only": x, "os": "ubuntu-latest"})' } | pyp 'json.dumps(list(map(json.loads, lines)))' > /tmp/matrix env: - CIBW_BUILD: "cp38-* cp311-*" + CIBW_BUILD: "cp38-* cp312-*" CIBW_ARCHS_LINUX: x86_64 - id: set-matrix run: echo "include=$(cat /tmp/matrix)" | tee -a $GITHUB_OUTPUT diff --git a/CHANGES.md b/CHANGES.md index f6fe69b..e9ffd6b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -27,6 +27,7 @@ ### Packaging - Upgrade to mypy 1.7.1 (#4049) (#4069) +- Faster compiled wheels are now available for CPython 3.12 (#4070) ### Parser diff --git a/pyproject.toml b/pyproject.toml index 6b681e8..1098412 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -150,7 +150,7 @@ build-verbosity = 1 # - Architecture (64-bit only): amd64 / x86_64, universal2, and arm64 # - OS: Linux (no musl), Windows, and macOS build = "cp3*" -skip = ["*-manylinux_i686", "*-musllinux_*", "*-win32", "pp*", "cp312-*"] +skip = ["*-manylinux_i686", "*-musllinux_*", "*-win32", "pp*"] # This is the bare minimum needed to run the test suite. Pulling in the full # test_requirements.txt would download a bunch of other packages not necessary # here and would slow down the testing step a fair bit. From 66ec056e39da957d3c82da5b7a86ef228606cfe6 Mon Sep 17 00:00:00 2001 From: exag Date: Mon, 4 Dec 2023 14:47:30 +0900 Subject: [PATCH 05/12] Fix minor typos in docstrings (#4085) --- src/black/numerics.py | 2 +- src/black/ranges.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/black/numerics.py b/src/black/numerics.py index 67ac859..3040de0 100644 --- a/src/black/numerics.py +++ b/src/black/numerics.py @@ -14,7 +14,7 @@ def format_hex(text: str) -> str: def format_scientific_notation(text: str) -> str: - """Formats a numeric string utilizing scentific notation""" + """Formats a numeric string utilizing scientific notation""" before, after = text.split("e") sign = "" if after.startswith("-"): diff --git a/src/black/ranges.py b/src/black/ranges.py index b0c312e..59e1924 100644 --- a/src/black/ranges.py +++ b/src/black/ranges.py @@ -172,7 +172,7 @@ class _TopLevelStatementsVisitor(Visitor[None]): A node visitor that converts unchanged top-level statements to STANDALONE_COMMENT. - This is used in addition to _convert_unchanged_lines_by_flatterning, to + This is used in addition to _convert_unchanged_line_by_line, to speed up formatting when there are unchanged top-level classes/functions/statements. """ @@ -302,7 +302,7 @@ def _convert_node_to_standalone_comment(node: LN) -> None: index = node.remove() if index is not None: # Remove the '\n', as STANDALONE_COMMENT will have '\n' appended when - # genearting the formatted code. + # generating the formatted code. value = str(node)[:-1] parent.insert_child( index, From 3416b2c82d51f27ce55c31ef0bfe4a9e21816611 Mon Sep 17 00:00:00 2001 From: Riyazuddin Khan Date: Mon, 4 Dec 2023 23:40:03 +0530 Subject: [PATCH 06/12] Fix: --line-ranges dedents a # fmt: off in the middle of a decorator (#4084) Fixes #4068 --- CHANGES.md | 3 ++- src/black/__init__.py | 2 +- src/black/comments.py | 23 ++++++++++++++---- .../cases/line_ranges_fmt_off_decorator.py | 24 +++++++++++++++++-- 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e9ffd6b..f17cd7f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,7 +8,8 @@ ### Stable style - +- Fix bug where `# fmt: off` automatically dedents when used with the `--line-ranges` + option, even when it is not within the specified line range. (#4084) ### Preview style diff --git a/src/black/__init__.py b/src/black/__init__.py index b33beee..04f6d8c 100644 --- a/src/black/__init__.py +++ b/src/black/__init__.py @@ -1180,7 +1180,7 @@ def _format_str_once( for feature in {Feature.PARENTHESIZED_CONTEXT_MANAGERS} if supports_feature(versions, feature) } - normalize_fmt_off(src_node, mode) + normalize_fmt_off(src_node, mode, lines) if lines: # This should be called after normalize_fmt_off. convert_unchanged_lines(src_node, lines) diff --git a/src/black/comments.py b/src/black/comments.py index 8a0e925..2541312 100644 --- a/src/black/comments.py +++ b/src/black/comments.py @@ -1,7 +1,7 @@ import re from dataclasses import dataclass from functools import lru_cache -from typing import Final, Iterator, List, Optional, Union +from typing import Collection, Final, Iterator, List, Optional, Tuple, Union from black.mode import Mode, Preview from black.nodes import ( @@ -161,14 +161,18 @@ def make_comment(content: str) -> str: return "#" + content -def normalize_fmt_off(node: Node, mode: Mode) -> None: +def normalize_fmt_off( + node: Node, mode: Mode, lines: Collection[Tuple[int, int]] +) -> None: """Convert content between `# fmt: off`/`# fmt: on` into standalone comments.""" try_again = True while try_again: - try_again = convert_one_fmt_off_pair(node, mode) + try_again = convert_one_fmt_off_pair(node, mode, lines) -def convert_one_fmt_off_pair(node: Node, mode: Mode) -> bool: +def convert_one_fmt_off_pair( + node: Node, mode: Mode, lines: Collection[Tuple[int, int]] +) -> bool: """Convert content of a single `# fmt: off`/`# fmt: on` into a standalone comment. Returns True if a pair was converted. @@ -213,7 +217,18 @@ def convert_one_fmt_off_pair(node: Node, mode: Mode) -> bool: prefix[:previous_consumed] + "\n" * comment.newlines ) hidden_value = "".join(str(n) for n in ignored_nodes) + comment_lineno = leaf.lineno - comment.newlines if comment.value in FMT_OFF: + fmt_off_prefix = "" + if len(lines) > 0 and not any( + comment_lineno >= line[0] and comment_lineno <= line[1] + for line in lines + ): + # keeping indentation of comment by preserving original whitespaces. + fmt_off_prefix = prefix.split(comment.value)[0] + if "\n" in fmt_off_prefix: + fmt_off_prefix = fmt_off_prefix.split("\n")[-1] + standalone_comment_prefix += fmt_off_prefix hidden_value = comment.value + "\n" + hidden_value if _contains_fmt_skip_comment(comment.value, mode): hidden_value += " " + comment.value diff --git a/tests/data/cases/line_ranges_fmt_off_decorator.py b/tests/data/cases/line_ranges_fmt_off_decorator.py index 14aa1dd..065bf43 100644 --- a/tests/data/cases/line_ranges_fmt_off_decorator.py +++ b/tests/data/cases/line_ranges_fmt_off_decorator.py @@ -1,4 +1,4 @@ -# flags: --line-ranges=12-12 +# flags: --line-ranges=12-12 --line-ranges=21-21 # NOTE: If you need to modify this file, pay special attention to the --line-ranges= # flag above as it's formatting specifically these lines. @@ -11,9 +11,19 @@ class MyClass: def method(): print ( "str" ) + @decor( + a=1, + # fmt: off + b=(2, 3), + # fmt: on + ) + def func(): + pass + + # output -# flags: --line-ranges=12-12 +# flags: --line-ranges=12-12 --line-ranges=21-21 # NOTE: If you need to modify this file, pay special attention to the --line-ranges= # flag above as it's formatting specifically these lines. @@ -25,3 +35,13 @@ class MyClass: # fmt: on def method(): print("str") + + @decor( + a=1, + # fmt: off + b=(2, 3), + # fmt: on + ) + def func(): + pass + From 50d5756e8e63b17e4523f096f312011273ce640f Mon Sep 17 00:00:00 2001 From: John Litborn <11260241+jakkdl@users.noreply.github.com> Date: Tue, 5 Dec 2023 19:19:24 +0100 Subject: [PATCH 07/12] fix crash in preview mode with --line-length=1 (#4086) --- CHANGES.md | 1 + src/black/linegen.py | 2 +- .../return_annotation_brackets_crash_line_length_1.py | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 tests/data/cases/return_annotation_brackets_crash_line_length_1.py diff --git a/CHANGES.md b/CHANGES.md index f17cd7f..8f0b75e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,7 @@ - Standalone form feed characters at the module level are no longer removed (#4021) - Additional cases of immediately nested tuples, lists, and dictionaries are now indented less (#4012) +- Fix crash in preview mode when using a short `--line-length` (#4086) ### Configuration diff --git a/src/black/linegen.py b/src/black/linegen.py index 7152568..073672a 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -744,7 +744,7 @@ def left_hand_split( if leaf.type in OPENING_BRACKETS: matching_bracket = leaf current_leaves = body_leaves - if not matching_bracket: + if not matching_bracket or not tail_leaves: raise CannotSplit("No brackets found") head = bracket_split_build_line( diff --git a/tests/data/cases/return_annotation_brackets_crash_line_length_1.py b/tests/data/cases/return_annotation_brackets_crash_line_length_1.py new file mode 100644 index 0000000..9d96b4a --- /dev/null +++ b/tests/data/cases/return_annotation_brackets_crash_line_length_1.py @@ -0,0 +1,9 @@ +# flags: --preview --minimum-version=3.10 --line-length=1 + +def foo() -> tuple[int, int,]: + ... +# output +def foo() -> tuple[ + int, + int, +]: ... From e4ae213f06050e7f76ebcf01578c002e412dafdc Mon Sep 17 00:00:00 2001 From: John Litborn <11260241+jakkdl@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:17:33 +0100 Subject: [PATCH 08/12] test preview cases with line-length 1 unless explicitly skipped (#4087) * Add new flag for tests, --no-preview-line-length-1, to be used for test cases known to not work in preview mode with line-length=1. Also split out the problematic cases in three cases to separate files. Removed now redundant file which explicitly tested preview annotations with line-length=1 * mode.preview -> preview_mode, mark pep_572_remove_parens as failing with ll1 --- tests/data/cases/comment_type_hint.py | 3 + tests/data/cases/comments2.py | 4 - tests/data/cases/fmtskip2.py | 5 +- tests/data/cases/pep_572_remove_parens.py | 2 +- ..._parens_with_braces_and_square_brackets.py | 96 ---------------- ..._with_braces_and_square_brackets_no_ll1.py | 106 ++++++++++++++++++ ...annotation_brackets_crash_line_length_1.py | 9 -- tests/test_format.py | 2 + tests/util.py | 54 ++++++--- 9 files changed, 154 insertions(+), 127 deletions(-) create mode 100644 tests/data/cases/comment_type_hint.py create mode 100644 tests/data/cases/preview_hug_parens_with_braces_and_square_brackets_no_ll1.py delete mode 100644 tests/data/cases/return_annotation_brackets_crash_line_length_1.py diff --git a/tests/data/cases/comment_type_hint.py b/tests/data/cases/comment_type_hint.py new file mode 100644 index 0000000..2992da8 --- /dev/null +++ b/tests/data/cases/comment_type_hint.py @@ -0,0 +1,3 @@ +# flags: --no-preview-line-length-1 +# split out from comments2 as it does not work with line-length=1, losing the comment +a = "type comment with trailing space" # type: str diff --git a/tests/data/cases/comments2.py b/tests/data/cases/comments2.py index 1487dc4..261c5e9 100644 --- a/tests/data/cases/comments2.py +++ b/tests/data/cases/comments2.py @@ -155,8 +155,6 @@ def _init_host(self, parsed) -> None: pass -a = "type comment with trailing space" # type: str - ####################### ### SECTION COMMENT ### ####################### @@ -335,8 +333,6 @@ def _init_host(self, parsed) -> None: pass -a = "type comment with trailing space" # type: str - ####################### ### SECTION COMMENT ### ####################### diff --git a/tests/data/cases/fmtskip2.py b/tests/data/cases/fmtskip2.py index e624811..0189d4e 100644 --- a/tests/data/cases/fmtskip2.py +++ b/tests/data/cases/fmtskip2.py @@ -1,9 +1,12 @@ +# flags: --no-preview-line-length-1 +# l2 loses the comment with line-length=1 in preview mode l1 = ["This list should be broken up", "into multiple lines", "because it is way too long"] l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"] # fmt: skip l3 = ["I have", "trailing comma", "so I should be braked",] # output +# l2 loses the comment with line-length=1 in preview mode l1 = [ "This list should be broken up", "into multiple lines", @@ -14,4 +17,4 @@ "I have", "trailing comma", "so I should be braked", -] \ No newline at end of file +] diff --git a/tests/data/cases/pep_572_remove_parens.py b/tests/data/cases/pep_572_remove_parens.py index 24f1ac2..88774d8 100644 --- a/tests/data/cases/pep_572_remove_parens.py +++ b/tests/data/cases/pep_572_remove_parens.py @@ -1,4 +1,4 @@ -# flags: --minimum-version=3.8 +# flags: --minimum-version=3.8 --no-preview-line-length-1 if (foo := 0): pass 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 9e5c9eb..47a6a0b 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 @@ -125,23 +125,6 @@ def foo_square_brackets(request): func([x for x in "long line long line long line long line long line long line long line"]) func([x for x in [x for x in "long line long line long line long line long line long line long line"]]) -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} ) @@ -151,14 +134,11 @@ 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"]) - foo(*[str(i) for i in range(100000000000000000000000000000000000000000000000000000000000)]) foo( @@ -310,69 +290,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} ) @@ -387,13 +304,6 @@ def foo_square_brackets(request): "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 = [ [ [ @@ -406,12 +316,6 @@ def foo_square_brackets(request): "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) ]) diff --git a/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets_no_ll1.py b/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets_no_ll1.py new file mode 100644 index 0000000..fdebdf6 --- /dev/null +++ b/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets_no_ll1.py @@ -0,0 +1,106 @@ +# flags: --preview --no-preview-line-length-1 +# split out from preview_hug_parens_with_brackes_and_square_brackets, as it produces +# different code on the second pass with line-length 1 in many cases. +# Seems to be about whether the last string in a sequence gets wrapped in parens or not. +foo(*["long long long long long line", "long long long long long line", "long long long long long line"]) +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"]] + +nested_array = [[["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]] + +# output + +# split out from preview_hug_parens_with_brackes_and_square_brackets, as it produces +# different code on the second pass with line-length 1 in many cases. +# Seems to be about whether the last string in a sequence gets wrapped in parens or not. +foo(*[ + "long long long long long line", + "long long long long long line", + "long long long long long line", +]) +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"] +] + +nested_array = [[[ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +]]] diff --git a/tests/data/cases/return_annotation_brackets_crash_line_length_1.py b/tests/data/cases/return_annotation_brackets_crash_line_length_1.py deleted file mode 100644 index 9d96b4a..0000000 --- a/tests/data/cases/return_annotation_brackets_crash_line_length_1.py +++ /dev/null @@ -1,9 +0,0 @@ -# flags: --preview --minimum-version=3.10 --line-length=1 - -def foo() -> tuple[int, int,]: - ... -# output -def foo() -> tuple[ - int, - int, -]: ... diff --git a/tests/test_format.py b/tests/test_format.py index 6c2eca8..9162c58 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -30,6 +30,7 @@ def check_file(subdir: str, filename: str, *, data: bool = True) -> None: fast=args.fast, minimum_version=args.minimum_version, lines=args.lines, + no_preview_line_length_1=args.no_preview_line_length_1, ) if args.minimum_version is not None: major, minor = args.minimum_version @@ -42,6 +43,7 @@ def check_file(subdir: str, filename: str, *, data: bool = True) -> None: fast=args.fast, minimum_version=args.minimum_version, lines=args.lines, + no_preview_line_length_1=args.no_preview_line_length_1, ) diff --git a/tests/util.py b/tests/util.py index c8699d3..9ea30e6 100644 --- a/tests/util.py +++ b/tests/util.py @@ -46,6 +46,7 @@ class TestCaseArgs: fast: bool = False minimum_version: Optional[Tuple[int, int]] = None lines: Collection[Tuple[int, int]] = () + no_preview_line_length_1: bool = False def _assert_format_equal(expected: str, actual: str) -> None: @@ -96,6 +97,7 @@ def assert_format( fast: bool = False, minimum_version: Optional[Tuple[int, int]] = None, lines: Collection[Tuple[int, int]] = (), + no_preview_line_length_1: bool = False, ) -> None: """Convenience function to check that Black formats as expected. @@ -124,21 +126,28 @@ def assert_format( f"Black crashed formatting this case in {text} mode." ) from e # Similarly, setting line length to 1 is a good way to catch - # stability bugs. But only in non-preview mode because preview mode - # currently has a lot of line length 1 bugs. - try: - _assert_format_inner( - source, - None, - replace(mode, preview=False, line_length=1), - fast=fast, - minimum_version=minimum_version, - lines=lines, - ) - except Exception as e: - raise FormatFailure( - "Black crashed formatting this case with line-length set to 1." - ) from e + # stability bugs. Some tests are known to be broken in preview mode with line length + # of 1 though, and have marked that with a flag --no-preview-line-length-1 + preview_modes = [False] + if not no_preview_line_length_1: + preview_modes.append(True) + + for preview_mode in preview_modes: + + try: + _assert_format_inner( + source, + None, + replace(mode, preview=preview_mode, line_length=1), + fast=fast, + minimum_version=minimum_version, + lines=lines, + ) + except Exception as e: + text = "preview" if preview_mode else "non-preview" + raise FormatFailure( + f"Black crashed formatting this case in {text} mode with line-length=1." + ) from e def _assert_format_inner( @@ -246,6 +255,15 @@ def get_flags_parser() -> argparse.ArgumentParser: ), ) parser.add_argument("--line-ranges", action="append") + parser.add_argument( + "--no-preview-line-length-1", + default=False, + action="store_true", + help=( + "Don't run in preview mode with --line-length=1, as that's known to cause a" + " crash" + ), + ) return parser @@ -266,7 +284,11 @@ def parse_mode(flags_line: str) -> TestCaseArgs: else: lines = [] return TestCaseArgs( - mode=mode, fast=args.fast, minimum_version=args.minimum_version, lines=lines + mode=mode, + fast=args.fast, + minimum_version=args.minimum_version, + lines=lines, + no_preview_line_length_1=args.no_preview_line_length_1, ) From 50e287cecea41ee32bd66ab1eee4827f6b8312ce Mon Sep 17 00:00:00 2001 From: cobalt <61329810+RedGuy12@users.noreply.github.com> Date: Thu, 7 Dec 2023 10:38:57 -0600 Subject: [PATCH 09/12] docs: Clarify include/exclude documentation (#4072) --- docs/usage_and_configuration/the_basics.md | 22 +++++++-------- src/black/__init__.py | 33 +++++++++++----------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/docs/usage_and_configuration/the_basics.md b/docs/usage_and_configuration/the_basics.md index 0c1a4d3..3739bca 100644 --- a/docs/usage_and_configuration/the_basics.md +++ b/docs/usage_and_configuration/the_basics.md @@ -241,24 +241,17 @@ Because of our [stability policy](../the_black_code_style/index.md), this will g stable formatting, but still allow you to take advantage of improvements that do not affect formatting. -#### `--include` - -A regular expression that matches files and directories that should be included on -recursive searches. An empty value means all files are included regardless of the name. -Use forward slashes for directories on all platforms (Windows, too). Exclusions are -calculated first, inclusions later. - #### `--exclude` A regular expression that matches files and directories that should be excluded on recursive searches. An empty value means no paths are excluded. Use forward slashes for -directories on all platforms (Windows, too). Exclusions are calculated first, inclusions -later. +directories on all platforms (Windows, too). By default, Black also ignores all paths +listed in `.gitignore`. Changing this value will override all default exclusions. #### `--extend-exclude` -Like `--exclude`, but adds additional files and directories on top of the excluded ones. -Useful if you simply want to add to the default. +Like `--exclude`, but adds additional files and directories on top of the default values +instead of overriding them. #### `--force-exclude` @@ -271,6 +264,13 @@ programmatically on changed files, such as in a pre-commit hook or editor plugin The name of the file when passing it through stdin. Useful to make sure Black will respect the `--force-exclude` option on some editors that rely on using stdin. +#### `--include` + +A regular expression that matches files and directories that should be included on +recursive searches. An empty value means all files are included regardless of the name. +Use forward slashes for directories on all platforms (Windows, too). Overrides all +exclusions, including from `.gitignore` and command line options. + #### `-W`, `--workers` When _Black_ formats multiple files, it may use a process pool to speed up formatting. diff --git a/src/black/__init__.py b/src/black/__init__.py index 04f6d8c..e7dac89 100644 --- a/src/black/__init__.py +++ b/src/black/__init__.py @@ -344,19 +344,6 @@ def validate_regex( " either a major version number or an exact version." ), ) -@click.option( - "--include", - type=str, - default=DEFAULT_INCLUDES, - callback=validate_regex, - help=( - "A regular expression that matches files and directories that should be" - " included on recursive searches. An empty value means all files are included" - " regardless of the name. Use forward slashes for directories on all platforms" - " (Windows, too). Exclusions are calculated first, inclusions later." - ), - show_default=True, -) @click.option( "--exclude", type=str, @@ -365,8 +352,8 @@ def validate_regex( "A regular expression that matches files and directories that should be" " excluded on recursive searches. An empty value means no paths are excluded." " Use forward slashes for directories on all platforms (Windows, too)." - " Exclusions are calculated first, inclusions later. [default:" - f" {DEFAULT_EXCLUDES}]" + " By default, Black also ignores all paths listed in .gitignore. Changing this" + f" value will override all default exclusions. [default: {DEFAULT_EXCLUDES}]" ), show_default=False, ) @@ -376,7 +363,7 @@ def validate_regex( callback=validate_regex, help=( "Like --exclude, but adds additional files and directories on top of the" - " excluded ones. (Useful if you simply want to add to the default)" + " default values instead of overriding them." ), ) @click.option( @@ -398,6 +385,20 @@ def validate_regex( "editors that rely on using stdin." ), ) +@click.option( + "--include", + type=str, + default=DEFAULT_INCLUDES, + callback=validate_regex, + help=( + "A regular expression that matches files and directories that should be" + " included on recursive searches. An empty value means all files are included" + " regardless of the name. Use forward slashes for directories on all platforms" + " (Windows, too). Overrides all exclusions, including from .gitignore and" + " command line options." + ), + show_default=True, +) @click.option( "-W", "--workers", From 432d9050c3d1e35a36ffc97d4a9e0e0c9e5e4ecc Mon Sep 17 00:00:00 2001 From: cobalt <61329810+RedGuy12@users.noreply.github.com> Date: Thu, 7 Dec 2023 11:32:06 -0600 Subject: [PATCH 10/12] docs: Unify option descriptions between `--help` and `the_basics.md` (#4076) --- docs/usage_and_configuration/the_basics.md | 45 +++++++------ src/black/__init__.py | 75 +++++++++++++--------- 2 files changed, 69 insertions(+), 51 deletions(-) diff --git a/docs/usage_and_configuration/the_basics.md b/docs/usage_and_configuration/the_basics.md index 3739bca..73c0d13 100644 --- a/docs/usage_and_configuration/the_basics.md +++ b/docs/usage_and_configuration/the_basics.md @@ -35,6 +35,10 @@ are deliberately limited and rarely added. Note that all command-line options listed above can also be configured using a `pyproject.toml` file (more on that below). +#### `-h`, `--help` + +Show available command-line options and exit. + #### `-c`, `--code` Format the code passed in as a string. @@ -109,6 +113,10 @@ useful when piping source on standard input. When processing Jupyter Notebooks, add the given magic to the list of known python- magics. Useful for formatting cells with custom python magics. +#### `-x, --skip-source-first-line` + +Skip the first line of the source code. + #### `-S, --skip-string-normalization` By default, _Black_ uses double quotes for all strings and normalizes string prefixes, @@ -132,7 +140,7 @@ functionality in the next major release. Read more about #### `--check` -Passing `--check` will make _Black_ exit with: +Don't write the files back, just return the status. _Black_ will exit with: - code 0 if nothing would change; - code 1 if some files would be reformatted; or @@ -162,8 +170,8 @@ $ echo $? #### `--diff` -Passing `--diff` will make _Black_ print out diffs that indicate what changes _Black_ -would've made. They are printed to stdout so capturing them is simple. +Don't write the files back, just output a diff to indicate what changes _Black_ would've +made. They are printed to stdout so capturing them is simple. If you'd like colored diffs, you can enable them with `--color`. @@ -179,6 +187,10 @@ All done! ✨ 🍰 ✨ 1 file would be reformatted. ``` +#### `--color` / `--no-color` + +Show (or do not show) colored diff. Only applies when `--diff` is given. + ### `--line-ranges` When specified, _Black_ will try its best to only format these lines. @@ -202,10 +214,6 @@ extra lines outside of the ranges when ther are unformatted lines with the exact content. It also disables _Black_'s formatting stability check in `--safe` mode. ``` -#### `--color` / `--no-color` - -Show (or do not show) colored diff. Only applies when `--diff` is given. - #### `--fast` / `--safe` By default, _Black_ performs [an AST safety check](labels/ast-changes) after formatting @@ -256,7 +264,7 @@ instead of overriding them. #### `--force-exclude` Like `--exclude`, but files and directories matching this regex will be excluded even -when they are passed explicitly as arguments. This is useful when invoking _Black_ +when they are passed explicitly as arguments. This is useful when invoking Black programmatically on changed files, such as in a pre-commit hook or editor plugin. #### `--stdin-filename` @@ -275,12 +283,12 @@ exclusions, including from `.gitignore` and command line options. When _Black_ formats multiple files, it may use a process pool to speed up formatting. This option controls the number of parallel workers. This can also be specified via the -`BLACK_NUM_WORKERS` environment variable. +`BLACK_NUM_WORKERS` environment variable. Defaults to the number of CPUs in the system. #### `-q`, `--quiet` -Passing `-q` / `--quiet` will cause _Black_ to stop emitting all non-critical output. -Error messages will still be emitted (which can silenced by `2>/dev/null`). +Stop emitting all non-critical output. Error messages will still be emitted (which can +silenced by `2>/dev/null`). ```console $ black src/ -q @@ -289,9 +297,9 @@ error: cannot format src/black_primer/cli.py: Cannot parse: 5:6: mport asyncio #### `-v`, `--verbose` -Passing `-v` / `--verbose` will cause _Black_ to also emit messages about files that -were not changed or were ignored due to exclusion patterns. If _Black_ is using a -configuration file, a blue message detailing which one it is using will be emitted. +Emit messages about files that were not changed or were ignored due to exclusion +patterns. If _Black_ is using a configuration file, a message detailing which one it is +using will be emitted. ```console $ black src/ -v @@ -321,10 +329,6 @@ black, 23.11.0 Read configuration options from a configuration file. See [below](#configuration-via-a-file) for more details on the configuration file. -#### `-h`, `--help` - -Show available command-line options and exit. - ### Environment variable options _Black_ supports the following configuration via environment variables. @@ -355,7 +359,7 @@ All done! ✨ 🍰 ✨ use `--stdin-filename`. Useful to make sure _Black_ will respect the `--force-exclude` option on some editors that rely on using stdin. -You can also pass code as a string using the `-c` / `--code` option. +You can also pass code as a string using the `--code` option. ### Writeback and reporting @@ -435,8 +439,7 @@ refers to the path to your home directory. On Windows, this will be something li You can also explicitly specify the path to a particular file that you want with `--config`. In this situation _Black_ will not look for any other file. -If you're running with `--verbose`, you will see a blue message if a file was found and -used. +If you're running with `--verbose`, you will see a message if a file was found and used. Please note `blackd` will not use `pyproject.toml` configuration. diff --git a/src/black/__init__.py b/src/black/__init__.py index e7dac89..5073fa7 100644 --- a/src/black/__init__.py +++ b/src/black/__init__.py @@ -235,25 +235,26 @@ def validate_regex( callback=target_version_option_callback, multiple=True, help=( - "Python versions that should be supported by Black's output. By default, Black" - " will try to infer this from the project metadata in pyproject.toml. If this" - " does not yield conclusive results, Black will use per-file auto-detection." + "Python versions that should be supported by Black's output. You should" + " include all versions that your code supports. By default, Black will infer" + " target versions from the project metadata in pyproject.toml. If this does" + " not yield conclusive results, Black will use per-file auto-detection." ), ) @click.option( "--pyi", is_flag=True, help=( - "Format all input files like typing stubs regardless of file extension (useful" - " when piping source on standard input)." + "Format all input files like typing stubs regardless of file extension. This" + " is useful when piping source on standard input." ), ) @click.option( "--ipynb", is_flag=True, help=( - "Format all input files like Jupyter Notebooks regardless of file extension " - "(useful when piping source on standard input)." + "Format all input files like Jupyter Notebooks regardless of file extension." + "This is useful when piping source on standard input." ), ) @click.option( @@ -310,14 +311,22 @@ def validate_regex( @click.option( "--diff", is_flag=True, - help="Don't write the files back, just output a diff for each file on stdout.", + help=( + "Don't write the files back, just output a diff to indicate what changes" + " Black would've made. They are printed to stdout so capturing them is simple." + ), +) +@click.option( + "--color/--no-color", + is_flag=True, + help="Show (or do not show) colored diff. Only applies when --diff is given.", ) @click.option( "--line-ranges", multiple=True, metavar="START-END", help=( - "When specified, _Black_ will try its best to only format these lines. This" + "When specified, Black will try its best to only format these lines. This" " option can be specified multiple times, and a union of the lines will be" " formatted. Each range must be specified as two integers connected by a `-`:" " `-`. The `` and `` integer indices are 1-based and" @@ -325,23 +334,24 @@ def validate_regex( ), default=(), ) -@click.option( - "--color/--no-color", - is_flag=True, - help="Show colored diff. Only applies when `--diff` is given.", -) @click.option( "--fast/--safe", is_flag=True, - help="If --fast given, skip temporary sanity checks. [default: --safe]", + help=( + "By default, Black performs an AST safety check after formatting your code." + " The --fast flag turns off this check and the --safe flag explicitly enables" + " it. [default: --safe]" + ), ) @click.option( "--required-version", type=str, help=( - "Require a specific version of Black to be running (useful for unifying results" - " across many environments e.g. with a pyproject.toml file). It can be" - " either a major version number or an exact version." + "Require a specific version of Black to be running. This is useful for" + " ensuring that all contributors to your project are using the same" + " version, because different versions of Black may format code a little" + " differently. This option can be set in a configuration file for consistent" + " results across environments." ), ) @click.option( @@ -371,8 +381,10 @@ def validate_regex( type=str, callback=validate_regex, help=( - "Like --exclude, but files and directories matching this regex will be " - "excluded even when they are passed explicitly as arguments." + "Like --exclude, but files and directories matching this regex will be excluded" + " even when they are passed explicitly as arguments. This is useful when" + " invoking Black programmatically on changed files, such as in a pre-commit" + " hook or editor plugin." ), ) @click.option( @@ -380,9 +392,9 @@ def validate_regex( type=str, is_eager=True, help=( - "The name of the file when passing it through stdin. Useful to make " - "sure Black will respect --force-exclude option on some " - "editors that rely on using stdin." + "The name of the file when passing it through stdin. Useful to make sure Black" + " will respect the --force-exclude option on some editors that rely on using" + " stdin." ), ) @click.option( @@ -405,8 +417,10 @@ def validate_regex( type=click.IntRange(min=1), default=None, help=( - "Number of parallel workers [default: BLACK_NUM_WORKERS environment variable " - "or number of CPUs in the system]" + "When Black formats multiple files, it may use a process pool to speed up" + " formatting. This option controls the number of parallel workers. This can" + " also be specified via the BLACK_NUM_WORKERS environment variable. Defaults" + " to the number of CPUs in the system." ), ) @click.option( @@ -414,8 +428,8 @@ def validate_regex( "--quiet", is_flag=True, help=( - "Don't emit non-error messages to stderr. Errors are still emitted; silence" - " those with 2>/dev/null." + "Stop emitting all non-critical output. Error messages will still be emitted" + " (which can silenced by 2>/dev/null)." ), ) @click.option( @@ -423,8 +437,9 @@ def validate_regex( "--verbose", is_flag=True, help=( - "Also emit messages to stderr about files that were not changed or were ignored" - " due to exclusion patterns." + "Emit messages about files that were not changed or were ignored due to" + " exclusion patterns. If Black is using a configuration file, a message" + " detailing which one it is using will be emitted." ), ) @click.version_option( @@ -455,7 +470,7 @@ def validate_regex( ), is_eager=True, callback=read_pyproject_toml, - help="Read configuration from FILE path.", + help="Read configuration options from a configuration file.", ) @click.pass_context def main( # noqa: C901 From e7e122e9ff27fc040a6e8ecd92f0e7603c87f92d Mon Sep 17 00:00:00 2001 From: cobalt <61329810+RedGuy12@users.noreply.github.com> Date: Sat, 9 Dec 2023 19:44:15 -0600 Subject: [PATCH 11/12] docs: Move `fmt: off` docs (#4090) --- docs/the_black_code_style/current_style.md | 15 +++------------ docs/usage_and_configuration/the_basics.md | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/docs/the_black_code_style/current_style.md b/docs/the_black_code_style/current_style.md index 2a5e101..00bd814 100644 --- a/docs/the_black_code_style/current_style.md +++ b/docs/the_black_code_style/current_style.md @@ -8,18 +8,9 @@ deliberately limited and rarely added. Previous formatting is taken into account little as possible, with rare exceptions like the magic trailing comma. The coding style used by _Black_ can be viewed as a strict subset of PEP 8. -_Black_ reformats entire files in place. It doesn't reformat lines that contain -`# fmt: skip` or blocks that start with `# fmt: off` and end with `# fmt: on`. -`# fmt: skip` can be mixed with other pragmas/comments either with multiple comments -(e.g. `# fmt: skip # pylint # noqa`) or as a semicolon separated list (e.g. -`# fmt: skip; pylint; noqa`). `# fmt: on/off` must be on the same level of indentation -and in the same block, meaning no unindents beyond the initial indentation level between -them. It also recognizes [YAPF](https://github.com/google/yapf)'s block comments to the -same effect, as a courtesy for straddling code. - -The rest of this document describes the current formatting style. If you're interested -in trying out where the style is heading, see [future style](./future_style.md) and try -running `black --preview`. +This document describes the current formatting style. If you're interested in trying out +where the style is heading, see [future style](./future_style.md) and try running +`black --preview`. ### How _Black_ wraps lines diff --git a/docs/usage_and_configuration/the_basics.md b/docs/usage_and_configuration/the_basics.md index 73c0d13..eb92887 100644 --- a/docs/usage_and_configuration/the_basics.md +++ b/docs/usage_and_configuration/the_basics.md @@ -12,7 +12,8 @@ _Black_ is a well-behaved Unix-style command-line tool: ## Usage -To get started right away with sensible defaults: +_Black_ will reformat entire files in place. To get started right away with sensible +defaults: ```sh black {source_file_or_directory} @@ -24,6 +25,17 @@ You can run _Black_ as a package if running it as a script doesn't work: python -m black {source_file_or_directory} ``` +### Ignoring sections + +Black will not reformat lines that contain `# fmt: skip` or blocks that start with +`# fmt: off` and end with `# fmt: on`. `# fmt: skip` can be mixed with other +pragmas/comments either with multiple comments (e.g. `# fmt: skip # pylint # noqa`) or +as a semicolon separated list (e.g. `# fmt: skip; pylint; noqa`). `# fmt: on/off` must +be on the same level of indentation and in the same block, meaning no unindents beyond +the initial indentation level between them. Black also recognizes +[YAPF](https://github.com/google/yapf)'s block comments to the same effect, as a +courtesy for straddling code. + ### Command line options The CLI options of _Black_ can be displayed by running `black --help`. All options are @@ -191,7 +203,7 @@ All done! ✨ 🍰 ✨ Show (or do not show) colored diff. Only applies when `--diff` is given. -### `--line-ranges` +#### `--line-ranges` When specified, _Black_ will try its best to only format these lines. From 61b529b7d15400309379f36104885a1dfcd2d026 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 9 Dec 2023 18:29:09 -0800 Subject: [PATCH 12/12] Allow empty lines at beginning of blocks (again) (#4060) --- CHANGES.md | 2 ++ src/black/lines.py | 14 +++++------- src/black/mode.py | 2 +- ...s.py => preview_allow_empty_first_line.py} | 22 +++++++++++++++++++ tests/data/cases/preview_form_feeds.py | 1 + 5 files changed, 31 insertions(+), 10 deletions(-) rename tests/data/cases/{preview_allow_empty_first_line_in_special_cases.py => preview_allow_empty_first_line.py} (87%) diff --git a/CHANGES.md b/CHANGES.md index 8f0b75e..fa0d249 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,8 @@ - Standalone form feed characters at the module level are no longer removed (#4021) - Additional cases of immediately nested tuples, lists, and dictionaries are now indented less (#4012) +- Allow empty lines at the beginning of all blocks, except immediately before a + docstring (#4060) - Fix crash in preview mode when using a short `--line-length` (#4086) ### Configuration diff --git a/src/black/lines.py b/src/black/lines.py index 6e33ee5..4050f81 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -689,18 +689,14 @@ def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]: return 0, 1 return before, 1 + # In preview mode, always allow blank lines, except right before a function + # docstring is_empty_first_line_ok = ( - Preview.allow_empty_first_line_before_new_block_or_comment - in current_line.mode + Preview.allow_empty_first_line_in_block in current_line.mode and ( - # If it's a standalone comment - current_line.leaves[0].type == STANDALONE_COMMENT - # If it opens a new block - or current_line.opens_block - # If it's a triple quote comment (but not at the start of a funcdef) + not is_docstring(current_line.leaves[0]) or ( - is_docstring(current_line.leaves[0]) - and self.previous_line + self.previous_line and self.previous_line.leaves[0] and self.previous_line.leaves[0].parent and not is_funcdef(self.previous_line.leaves[0].parent) diff --git a/src/black/mode.py b/src/black/mode.py index 04038f4..9df1961 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -191,7 +191,7 @@ class Preview(Enum): accept_raw_docstrings = auto() fix_power_op_line_length = auto() hug_parens_with_braces_and_square_brackets = auto() - allow_empty_first_line_before_new_block_or_comment = auto() + allow_empty_first_line_in_block = auto() single_line_format_skip_with_multiple_comments = auto() long_case_block_line_splitting = auto() allow_form_feeds = auto() diff --git a/tests/data/cases/preview_allow_empty_first_line_in_special_cases.py b/tests/data/cases/preview_allow_empty_first_line.py similarity index 87% rename from tests/data/cases/preview_allow_empty_first_line_in_special_cases.py rename to tests/data/cases/preview_allow_empty_first_line.py index 96c1433..3e14fa1 100644 --- a/tests/data/cases/preview_allow_empty_first_line_in_special_cases.py +++ b/tests/data/cases/preview_allow_empty_first_line.py @@ -51,6 +51,17 @@ def baz(): if x: a = 123 +def quux(): + + new_line = here + + +class Cls: + + def method(self): + + pass + # output def foo(): @@ -104,3 +115,14 @@ def baz(): # OK if x: a = 123 + + +def quux(): + + new_line = here + + +class Cls: + def method(self): + + pass diff --git a/tests/data/cases/preview_form_feeds.py b/tests/data/cases/preview_form_feeds.py index 2d8653a..c236f17 100644 --- a/tests/data/cases/preview_form_feeds.py +++ b/tests/data/cases/preview_form_feeds.py @@ -198,6 +198,7 @@ def foo(): # form feeds are prohibited inside blocks, or on a line with nonwhitespace def bar(a=1, b: bool = False): + pass