From f58f4466407b1570510be2ee9c28ea3ff717bb6e Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 08:36:35 -0800 Subject: [PATCH 01/32] Clean up comments.py preview mode --- src/black/__init__.py | 2 +- src/black/comments.py | 54 +++++++++++++++++++------------------------ src/black/linegen.py | 4 ++-- 3 files changed, 27 insertions(+), 33 deletions(-) diff --git a/src/black/__init__.py b/src/black/__init__.py index 39d12968c4b..367806721e2 100644 --- a/src/black/__init__.py +++ b/src/black/__init__.py @@ -1088,7 +1088,7 @@ def _format_str_once(src_contents: str, *, mode: Mode) -> str: future_imports = get_future_imports(src_node) versions = detect_target_versions(src_node, future_imports=future_imports) - normalize_fmt_off(src_node, preview=mode.preview) + normalize_fmt_off(src_node) lines = LineGenerator(mode=mode) elt = EmptyLineTracker(mode=mode) split_line_features = { diff --git a/src/black/comments.py b/src/black/comments.py index dce83abf1bb..3fbc7e4318b 100644 --- a/src/black/comments.py +++ b/src/black/comments.py @@ -29,7 +29,7 @@ FMT_PASS: Final = {*FMT_OFF, *FMT_SKIP} FMT_ON: Final = {"# fmt: on", "# fmt:on", "# yapf: enable"} -COMMENT_EXCEPTIONS = {True: " !:#'", False: " !:#'%"} +COMMENT_EXCEPTIONS = " !:#'" @dataclass @@ -50,7 +50,7 @@ class ProtoComment: consumed: int # how many characters of the original leaf's prefix did we consume -def generate_comments(leaf: LN, *, preview: bool) -> Iterator[Leaf]: +def generate_comments(leaf: LN) -> Iterator[Leaf]: """Clean the prefix of the `leaf` and generate comments from it, if any. Comments in lib2to3 are shoved into the whitespace prefix. This happens @@ -69,16 +69,12 @@ def generate_comments(leaf: LN, *, preview: bool) -> Iterator[Leaf]: Inline comments are emitted as regular token.COMMENT leaves. Standalone are emitted with a fake STANDALONE_COMMENT token identifier. """ - for pc in list_comments( - leaf.prefix, is_endmarker=leaf.type == token.ENDMARKER, preview=preview - ): + for pc in list_comments(leaf.prefix, is_endmarker=leaf.type == token.ENDMARKER): yield Leaf(pc.type, pc.value, prefix="\n" * pc.newlines) @lru_cache(maxsize=4096) -def list_comments( - prefix: str, *, is_endmarker: bool, preview: bool -) -> List[ProtoComment]: +def list_comments(prefix: str, *, is_endmarker: bool) -> List[ProtoComment]: """Return a list of :class:`ProtoComment` objects parsed from the given `prefix`.""" result: List[ProtoComment] = [] if not prefix or "#" not in prefix: @@ -104,7 +100,7 @@ def list_comments( comment_type = token.COMMENT # simple trailing comment else: comment_type = STANDALONE_COMMENT - comment = make_comment(line, preview=preview) + comment = make_comment(line) result.append( ProtoComment( type=comment_type, value=comment, newlines=nlines, consumed=consumed @@ -114,7 +110,7 @@ def list_comments( return result -def make_comment(content: str, *, preview: bool) -> str: +def make_comment(content: str) -> str: """Return a consistently formatted comment from the given `content` string. All comments (except for "##", "#!", "#:", '#'") should have a single @@ -135,26 +131,26 @@ def make_comment(content: str, *, preview: bool) -> str: and not content.lstrip().startswith("type:") ): content = " " + content[1:] # Replace NBSP by a simple space - if content and content[0] not in COMMENT_EXCEPTIONS[preview]: + if content and content[0] not in COMMENT_EXCEPTIONS: content = " " + content return "#" + content -def normalize_fmt_off(node: Node, *, preview: bool) -> None: +def normalize_fmt_off(node: Node) -> 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, preview=preview) + try_again = convert_one_fmt_off_pair(node) -def convert_one_fmt_off_pair(node: Node, *, preview: bool) -> bool: +def convert_one_fmt_off_pair(node: Node) -> bool: """Convert content of a single `# fmt: off`/`# fmt: on` into a standalone comment. Returns True if a pair was converted. """ for leaf in node.leaves(): previous_consumed = 0 - for comment in list_comments(leaf.prefix, is_endmarker=False, preview=preview): + for comment in list_comments(leaf.prefix, is_endmarker=False): if comment.value not in FMT_PASS: previous_consumed = comment.consumed continue @@ -169,7 +165,7 @@ def convert_one_fmt_off_pair(node: Node, *, preview: bool) -> bool: if comment.value in FMT_SKIP and prev.type in WHITESPACE: continue - ignored_nodes = list(generate_ignored_nodes(leaf, comment, preview=preview)) + ignored_nodes = list(generate_ignored_nodes(leaf, comment)) if not ignored_nodes: continue @@ -214,26 +210,24 @@ def convert_one_fmt_off_pair(node: Node, *, preview: bool) -> bool: return False -def generate_ignored_nodes( - leaf: Leaf, comment: ProtoComment, *, preview: bool -) -> Iterator[LN]: +def generate_ignored_nodes(leaf: Leaf, comment: ProtoComment) -> Iterator[LN]: """Starting from the container of `leaf`, generate all leaves until `# fmt: on`. If comment is skip, returns leaf only. Stops at the end of the block. """ if comment.value in FMT_SKIP: - yield from _generate_ignored_nodes_from_fmt_skip(leaf, comment, preview=preview) + yield from _generate_ignored_nodes_from_fmt_skip(leaf, comment) return container: Optional[LN] = container_of(leaf) while container is not None and container.type != token.ENDMARKER: - if is_fmt_on(container, preview=preview): + if is_fmt_on(container): return # fix for fmt: on in children - if children_contains_fmt_on(container, preview=preview): + if children_contains_fmt_on(container): for child in container.children: - if isinstance(child, Leaf) and is_fmt_on(child, preview=preview): + if isinstance(child, Leaf) and is_fmt_on(child): if child.type in CLOSING_BRACKETS: # This means `# fmt: on` is placed at a different bracket level # than `# fmt: off`. This is an invalid use, but as a courtesy, @@ -241,7 +235,7 @@ def generate_ignored_nodes( # The alternative is to fail the formatting. yield child return - if children_contains_fmt_on(child, preview=preview): + if children_contains_fmt_on(child): return yield child else: @@ -254,14 +248,14 @@ def generate_ignored_nodes( def _generate_ignored_nodes_from_fmt_skip( - leaf: Leaf, comment: ProtoComment, *, preview: bool + leaf: Leaf, comment: ProtoComment ) -> Iterator[LN]: """Generate all leaves that should be ignored by the `# fmt: skip` from `leaf`.""" prev_sibling = leaf.prev_sibling parent = leaf.parent # Need to properly format the leaf prefix to compare it to comment.value, # which is also formatted - comments = list_comments(leaf.prefix, is_endmarker=False, preview=preview) + comments = list_comments(leaf.prefix, is_endmarker=False) if not comments or comment.value != comments[0].value: return if prev_sibling is not None: @@ -295,12 +289,12 @@ def _generate_ignored_nodes_from_fmt_skip( yield from iter(ignored_nodes) -def is_fmt_on(container: LN, preview: bool) -> bool: +def is_fmt_on(container: LN) -> bool: """Determine whether formatting is switched on within a container. Determined by whether the last `# fmt:` comment is `on` or `off`. """ fmt_on = False - for comment in list_comments(container.prefix, is_endmarker=False, preview=preview): + for comment in list_comments(container.prefix, is_endmarker=False): if comment.value in FMT_ON: fmt_on = True elif comment.value in FMT_OFF: @@ -308,11 +302,11 @@ def is_fmt_on(container: LN, preview: bool) -> bool: return fmt_on -def children_contains_fmt_on(container: LN, *, preview: bool) -> bool: +def children_contains_fmt_on(container: LN) -> bool: """Determine if children have formatting switched on.""" for child in container.children: leaf = first_leaf_of(child) - if leaf is not None and is_fmt_on(leaf, preview=preview): + if leaf is not None and is_fmt_on(leaf): return True return False diff --git a/src/black/linegen.py b/src/black/linegen.py index 219495e9a5e..29843e6ab3e 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -112,7 +112,7 @@ def visit_default(self, node: LN) -> Iterator[Line]: """Default `visit_*()` implementation. Recurses to children of `node`.""" if isinstance(node, Leaf): any_open_brackets = self.current_line.bracket_tracker.any_open_brackets() - for comment in generate_comments(node, preview=self.mode.preview): + for comment in generate_comments(node): if any_open_brackets: # any comment within brackets is subject to splitting self.current_line.append(comment) @@ -971,7 +971,7 @@ def normalize_invisible_parens( Standardizes on visible parentheses for single-element tuples, and keeps existing visible parentheses for other tuples and generator expressions. """ - for pc in list_comments(node.prefix, is_endmarker=False, preview=preview): + for pc in list_comments(node.prefix, is_endmarker=False): if pc.value in FMT_OFF: # This `node` has a prefix with `# fmt: off`, don't mess with parens. return From 2caca042c09b8dd156bdb5492fa92e5f5b430ab2 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 08:38:03 -0800 Subject: [PATCH 02/32] Clean up preview in __init__.py --- src/black/__init__.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/black/__init__.py b/src/black/__init__.py index 367806721e2..94652eccfd5 100644 --- a/src/black/__init__.py +++ b/src/black/__init__.py @@ -917,9 +917,6 @@ def format_file_contents(src_contents: str, *, fast: bool, mode: Mode) -> FileCo valid by calling :func:`assert_equivalent` and :func:`assert_stable` on it. `mode` is passed to :func:`format_str`. """ - if not mode.preview and not src_contents.strip(): - raise NothingChanged - if mode.is_ipynb: dst_contents = format_ipynb_string(src_contents, fast=fast, mode=mode) else: @@ -1014,7 +1011,7 @@ def format_ipynb_string(src_contents: str, *, fast: bool, mode: Mode) -> FileCon Operate cell-by-cell, only on code cells, only for Python notebooks. If the ``.ipynb`` originally had a trailing newline, it'll be preserved. """ - if mode.preview and not src_contents: + if not src_contents: raise NothingChanged trailing_newline = src_contents[-1] == "\n" @@ -1109,7 +1106,7 @@ def _format_str_once(src_contents: str, *, mode: Mode) -> str: dst_contents = [] for block in dst_blocks: dst_contents.extend(block.all_lines()) - if mode.preview and not dst_contents: + if not dst_contents: # Use decode_bytes to retrieve the correct source newline (CRLF or LF), # and check if normalized_content has more than one line normalized_content, _, newline = decode_bytes(src_contents.encode("utf-8")) From 9a686767c96da1fc13e2d1a38d7238a850f68d10 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 08:40:04 -0800 Subject: [PATCH 03/32] clean up normalize_invisible_parens preview --- src/black/linegen.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 29843e6ab3e..50a6bbdf3db 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -172,7 +172,7 @@ def visit_stmt( `parens` holds a set of string leaf values immediately after which invisible parens should be put. """ - normalize_invisible_parens(node, parens_after=parens, preview=self.mode.preview) + normalize_invisible_parens(node, parens_after=parens) for child in node.children: if is_name_token(child) and child.value in keywords: yield from self.line() @@ -208,7 +208,7 @@ def visit_funcdef(self, node: Node) -> Iterator[Line]: def visit_match_case(self, node: Node) -> Iterator[Line]: """Visit either a match or case statement.""" - normalize_invisible_parens(node, parens_after=set(), preview=self.mode.preview) + normalize_invisible_parens(node, parens_after=set()) yield from self.line() for child in node.children: @@ -960,9 +960,7 @@ def normalize_prefix(leaf: Leaf, *, inside_brackets: bool) -> None: leaf.prefix = "" -def normalize_invisible_parens( - node: Node, parens_after: Set[str], *, preview: bool -) -> None: +def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None: """Make existing optional parentheses invisible or create new ones. `parens_after` is a set of string leaf values immediately after which parens @@ -980,9 +978,7 @@ def normalize_invisible_parens( # Fixes a bug where invisible parens are not properly stripped from # assignment statements that contain type annotations. if isinstance(child, Node) and child.type == syms.annassign: - normalize_invisible_parens( - child, parens_after=parens_after, preview=preview - ) + normalize_invisible_parens(child, parens_after=parens_after) # Add parentheses around long tuple unpacking in assignments. if ( @@ -994,8 +990,7 @@ def normalize_invisible_parens( if check_lpar: if ( - preview - and child.type == syms.atom + child.type == syms.atom and node.type == syms.for_stmt and isinstance(child.prev_sibling, Leaf) and child.prev_sibling.type == token.NAME @@ -1007,7 +1002,7 @@ def normalize_invisible_parens( remove_brackets_around_comma=True, ): wrap_in_parentheses(node, child, visible=False) - elif preview and isinstance(child, Node) and node.type == syms.with_stmt: + elif isinstance(child, Node) and node.type == syms.with_stmt: remove_with_parens(child, node) elif child.type == syms.atom: if maybe_make_parens_invisible_in_atom( @@ -1043,7 +1038,7 @@ def normalize_invisible_parens( elif not (isinstance(child, Leaf) and is_multiline_string(child)): wrap_in_parentheses(node, child, visible=False) - comma_check = child.type == token.COMMA if preview else False + comma_check = child.type == token.COMMA check_lpar = isinstance(child, Leaf) and ( child.value in parens_after or comma_check From 00cd37169dcfc5c332967cb4510cee652772ac51 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 08:41:19 -0800 Subject: [PATCH 04/32] more in linegen.py --- src/black/linegen.py | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 50a6bbdf3db..e28050e3e99 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -330,24 +330,17 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: if is_docstring(leaf) and "\\\n" not in leaf.value: # We're ignoring docstrings with backslash newline escapes because changing # indentation of those changes the AST representation of the code. - if Preview.normalize_docstring_quotes_and_prefixes_properly in self.mode: - # There was a bug where --skip-string-normalization wouldn't stop us - # from normalizing docstring prefixes. To maintain stability, we can - # only address this buggy behaviour while the preview style is enabled. - if self.mode.string_normalization: - docstring = normalize_string_prefix(leaf.value) - # visit_default() does handle string normalization for us, but - # since this method acts differently depending on quote style (ex. - # see padding logic below), there's a possibility for unstable - # formatting as visit_default() is called *after*. To avoid a - # situation where this function formats a docstring differently on - # the second pass, normalize it early. - docstring = normalize_string_quotes(docstring) - else: - docstring = leaf.value - else: - # ... otherwise, we'll keep the buggy behaviour >.< + if self.mode.string_normalization: docstring = normalize_string_prefix(leaf.value) + # visit_default() does handle string normalization for us, but + # since this method acts differently depending on quote style (ex. + # see padding logic below), there's a possibility for unstable + # formatting as visit_default() is called *after*. To avoid a + # situation where this function formats a docstring differently on + # the second pass, normalize it early. + docstring = normalize_string_quotes(docstring) + else: + docstring = leaf.value prefix = get_string_prefix(docstring) docstring = docstring[len(prefix) :] # Remove the prefix quote_char = docstring[0] @@ -425,14 +418,8 @@ def __post_init__(self) -> None: self.visit_try_stmt = partial( v, keywords={"try", "except", "else", "finally"}, parens=Ø ) - if self.mode.preview: - self.visit_except_clause = partial( - v, keywords={"except"}, parens={"except"} - ) - self.visit_with_stmt = partial(v, keywords={"with"}, parens={"with"}) - else: - self.visit_except_clause = partial(v, keywords={"except"}, parens=Ø) - self.visit_with_stmt = partial(v, keywords={"with"}, parens=Ø) + self.visit_except_clause = partial(v, keywords={"except"}, parens={"except"}) + self.visit_with_stmt = partial(v, keywords={"with"}, parens={"with"}) self.visit_classdef = partial(v, keywords={"class"}, parens=Ø) self.visit_expr_stmt = partial(v, keywords=Ø, parens=ASSIGNMENTS) self.visit_return_stmt = partial(v, keywords={"return"}, parens={"return"}) From b6cd780453e7791f4ccace1be4c74ea603192a16 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 08:43:04 -0800 Subject: [PATCH 05/32] annotation_parens --- src/black/linegen.py | 39 ++++++++++++++++++--------------------- src/black/mode.py | 1 - 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index e28050e3e99..8846d771f9c 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -181,30 +181,27 @@ def visit_stmt( def visit_funcdef(self, node: Node) -> Iterator[Line]: """Visit function definition.""" - if Preview.annotation_parens not in self.mode: - yield from self.visit_stmt(node, keywords={"def"}, parens=set()) - else: - yield from self.line() + yield from self.line() - # Remove redundant brackets around return type annotation. - is_return_annotation = False - for child in node.children: - if child.type == token.RARROW: - is_return_annotation = True - elif is_return_annotation: - if child.type == syms.atom and child.children[0].type == token.LPAR: - if maybe_make_parens_invisible_in_atom( - child, - parent=node, - remove_brackets_around_comma=False, - ): - wrap_in_parentheses(node, child, visible=False) - else: + # Remove redundant brackets around return type annotation. + is_return_annotation = False + for child in node.children: + if child.type == token.RARROW: + is_return_annotation = True + elif is_return_annotation: + if child.type == syms.atom and child.children[0].type == token.LPAR: + if maybe_make_parens_invisible_in_atom( + child, + parent=node, + remove_brackets_around_comma=False, + ): wrap_in_parentheses(node, child, visible=False) - is_return_annotation = False + else: + wrap_in_parentheses(node, child, visible=False) + is_return_annotation = False - for child in node.children: - yield from self.visit(child) + for child in node.children: + yield from self.visit(child) def visit_match_case(self, node: Node) -> Iterator[Line]: """Visit either a match or case statement.""" diff --git a/src/black/mode.py b/src/black/mode.py index a3ce20b8619..db54b578105 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -149,7 +149,6 @@ def supports_feature(target_versions: Set[TargetVersion], feature: Feature) -> b class Preview(Enum): """Individual preview style features.""" - annotation_parens = auto() empty_lines_before_class_or_def_with_leading_comments = auto() handle_trailing_commas_in_head = auto() long_docstring_quotes_on_newline = auto() From 714af8bc27034bd4af1bf9ed862573be4d860b56 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 08:45:40 -0800 Subject: [PATCH 06/32] empty_lines_before_class_or_def_with_leading_comments --- src/black/lines.py | 4 +--- src/black/mode.py | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/black/lines.py b/src/black/lines.py index 08281bcf370..9963bc9cc02 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -628,9 +628,7 @@ def _maybe_empty_lines_for_class_or_def( ): slc = self.semantic_leading_comment if ( - Preview.empty_lines_before_class_or_def_with_leading_comments - in current_line.mode - and slc is not None + slc is not None and slc.previous_block is not None and not slc.previous_block.original_line.is_class and not slc.previous_block.original_line.opens_block diff --git a/src/black/mode.py b/src/black/mode.py index db54b578105..136f5c5743f 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -149,7 +149,6 @@ def supports_feature(target_versions: Set[TargetVersion], feature: Feature) -> b class Preview(Enum): """Individual preview style features.""" - empty_lines_before_class_or_def_with_leading_comments = auto() handle_trailing_commas_in_head = auto() long_docstring_quotes_on_newline = auto() normalize_docstring_quotes_and_prefixes_properly = auto() From fe271eda279214d6b0f8c81086366635dfd07dfa Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 08:46:28 -0800 Subject: [PATCH 07/32] handle_trailing_commas_in_head --- src/black/linegen.py | 5 +---- src/black/mode.py | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 8846d771f9c..2bbad96aabc 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -778,10 +778,7 @@ def bracket_split_build_line( break leaves_to_track: Set[LeafID] = set() - if ( - Preview.handle_trailing_commas_in_head in original.mode - and component is _BracketSplitComponent.head - ): + if component is _BracketSplitComponent.head: leaves_to_track = get_leaves_inside_matching_brackets(leaves) # Populate the line for leaf in leaves: diff --git a/src/black/mode.py b/src/black/mode.py index 136f5c5743f..3cc7412037f 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -149,7 +149,6 @@ def supports_feature(target_versions: Set[TargetVersion], feature: Feature) -> b class Preview(Enum): """Individual preview style features.""" - handle_trailing_commas_in_head = auto() long_docstring_quotes_on_newline = auto() normalize_docstring_quotes_and_prefixes_properly = auto() one_element_subscript = auto() From 92370825cede7ac7901d64405842226c8d710653 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 08:46:51 -0800 Subject: [PATCH 08/32] normalize_docstring_quotes_and_prefixes_properly (unused) --- src/black/mode.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/black/mode.py b/src/black/mode.py index 3cc7412037f..190ad259145 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -150,7 +150,6 @@ class Preview(Enum): """Individual preview style features.""" long_docstring_quotes_on_newline = auto() - normalize_docstring_quotes_and_prefixes_properly = auto() one_element_subscript = auto() remove_block_trailing_newline = auto() remove_redundant_parens = auto() From fc2acb2ed523341872a117bf1535a03004156c15 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 08:47:31 -0800 Subject: [PATCH 09/32] one_element_subscript --- src/black/lines.py | 3 +-- src/black/mode.py | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/black/lines.py b/src/black/lines.py index 9963bc9cc02..d9612491c2e 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -290,8 +290,7 @@ def has_magic_trailing_comma( if closing.type == token.RSQB: if ( - Preview.one_element_subscript in self.mode - and closing.parent + closing.parent and closing.parent.type == syms.trailer and closing.opening_bracket and is_one_sequence_between( diff --git a/src/black/mode.py b/src/black/mode.py index 190ad259145..dbc45d26394 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -150,7 +150,6 @@ class Preview(Enum): """Individual preview style features.""" long_docstring_quotes_on_newline = auto() - one_element_subscript = auto() remove_block_trailing_newline = auto() remove_redundant_parens = auto() string_processing = auto() From ecfa7b601955ff9586d9104b388778a86b52c89f Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 08:47:59 -0800 Subject: [PATCH 10/32] remove_block_trailing_newline --- src/black/lines.py | 6 +----- src/black/mode.py | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/black/lines.py b/src/black/lines.py index d9612491c2e..e9abdaa683a 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -590,11 +590,7 @@ def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]: ): return before, 1 - if ( - Preview.remove_block_trailing_newline in current_line.mode - and self.previous_line - and self.previous_line.opens_block - ): + if self.previous_line and self.previous_line.opens_block: return 0, 0 return before, 0 diff --git a/src/black/mode.py b/src/black/mode.py index dbc45d26394..a57e4149657 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -150,7 +150,6 @@ class Preview(Enum): """Individual preview style features.""" long_docstring_quotes_on_newline = auto() - remove_block_trailing_newline = auto() remove_redundant_parens = auto() string_processing = auto() skip_magic_trailing_comma_in_subscript = auto() From 11b0f1e963cea8f5e6fc733079d04296725ae082 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 08:49:35 -0800 Subject: [PATCH 11/32] remove_redundant_parens --- src/black/linegen.py | 3 +-- src/black/mode.py | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 2bbad96aabc..ea54705146e 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -287,8 +287,7 @@ def visit_power(self, node: Node) -> Iterator[Line]: ): wrap_in_parentheses(node, leaf) - if Preview.remove_redundant_parens in self.mode: - remove_await_parens(node) + remove_await_parens(node) yield from self.visit_default(node) diff --git a/src/black/mode.py b/src/black/mode.py index a57e4149657..aef9c1bcf10 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -150,7 +150,6 @@ class Preview(Enum): """Individual preview style features.""" long_docstring_quotes_on_newline = auto() - remove_redundant_parens = auto() string_processing = auto() skip_magic_trailing_comma_in_subscript = auto() From 0152d51be2deb52d8088dc4b4ec6e0908fd7e830 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 08:50:37 -0800 Subject: [PATCH 12/32] skip_magic_trailing_comma_in_subscript --- src/black/lines.py | 23 ++++++++++------------- src/black/mode.py | 1 - 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/black/lines.py b/src/black/lines.py index e9abdaa683a..d0a7e6dfe2a 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -275,8 +275,7 @@ def has_magic_trailing_comma( - it's not a single-element subscript Additionally, if ensure_removable: - it's not from square bracket indexing - (specifically, single-element square bracket indexing with - Preview.skip_magic_trailing_comma_in_subscript) + (specifically, single-element square bracket indexing) """ if not ( closing.type in CLOSING_BRACKETS @@ -308,18 +307,16 @@ def has_magic_trailing_comma( comma = self.leaves[-1] if comma.parent is None: return False - if Preview.skip_magic_trailing_comma_in_subscript in self.mode: - return ( - comma.parent.type != syms.subscriptlist - or closing.opening_bracket is None - or not is_one_sequence_between( - closing.opening_bracket, - closing, - self.leaves, - brackets=(token.LSQB, token.RSQB), - ) + return ( + comma.parent.type != syms.subscriptlist + or closing.opening_bracket is None + or not is_one_sequence_between( + closing.opening_bracket, + closing, + self.leaves, + brackets=(token.LSQB, token.RSQB), ) - return comma.parent.type == syms.listmaker + ) if self.is_import: return True diff --git a/src/black/mode.py b/src/black/mode.py index aef9c1bcf10..8510b94fd98 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -151,7 +151,6 @@ class Preview(Enum): long_docstring_quotes_on_newline = auto() string_processing = auto() - skip_magic_trailing_comma_in_subscript = auto() class Deprecated(UserWarning): From 4329c9f4d1112b4c71a1afc583e0b74b103cb10c Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 08:56:05 -0800 Subject: [PATCH 13/32] unused import --- src/black/lines.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/black/lines.py b/src/black/lines.py index d0a7e6dfe2a..4ff11bd32b6 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -14,7 +14,7 @@ ) from black.brackets import DOT_PRIORITY, BracketTracker -from black.mode import Mode, Preview +from black.mode import Mode from black.nodes import ( BRACKETS, CLOSING_BRACKETS, From e44aa092f26631ef221ce4b3552b324e29fc1993 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 09:00:33 -0800 Subject: [PATCH 14/32] fix path in test --- tests/test_black.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_black.py b/tests/test_black.py index dda10555c97..4ffed2932a1 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -419,7 +419,8 @@ def test_skip_magic_trailing_comma(self) -> None: msg = ( "Expected diff isn't equal to the actual. If you made changes to" " expression.py and this is an anticipated difference, overwrite" - f" tests/data/expression_skip_magic_trailing_comma.diff with {dump}" + " tests/data/miscellaneous/expression_skip_magic_trailing_comma.diff" + f" with {dump}" ) self.assertEqual(expected, actual, msg) From c4c7072c6079f0653570f31bedd91f16c1451633 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 09:01:02 -0800 Subject: [PATCH 15/32] expression_skip_magic_trailing_comma.diff --- .../miscellaneous/expression_skip_magic_trailing_comma.diff | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/data/miscellaneous/expression_skip_magic_trailing_comma.diff b/tests/data/miscellaneous/expression_skip_magic_trailing_comma.diff index eba3fd2da7d..d17467b15c7 100644 --- a/tests/data/miscellaneous/expression_skip_magic_trailing_comma.diff +++ b/tests/data/miscellaneous/expression_skip_magic_trailing_comma.diff @@ -144,8 +144,9 @@ -tuple[ - str, int, float, dict[str, int] -] +-tuple[str, int, float, dict[str, int],] ++tuple[str, int, float, dict[str, int]] +tuple[str, int, float, dict[str, int]] - tuple[str, int, float, dict[str, int],] very_long_variable_name_filters: t.List[ t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]], ] From 7fbdc950fe43dff90994534cc14658366772fdc9 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 10 Dec 2022 09:08:58 -0800 Subject: [PATCH 16/32] Revert "annotation_parens" This reverts commit b6cd780453e7791f4ccace1be4c74ea603192a16. --- src/black/linegen.py | 39 +++++++++++++++++++++------------------ src/black/mode.py | 1 + 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index ea54705146e..5901c8f2a52 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -181,27 +181,30 @@ def visit_stmt( def visit_funcdef(self, node: Node) -> Iterator[Line]: """Visit function definition.""" - yield from self.line() + if Preview.annotation_parens not in self.mode: + yield from self.visit_stmt(node, keywords={"def"}, parens=set()) + else: + yield from self.line() - # Remove redundant brackets around return type annotation. - is_return_annotation = False - for child in node.children: - if child.type == token.RARROW: - is_return_annotation = True - elif is_return_annotation: - if child.type == syms.atom and child.children[0].type == token.LPAR: - if maybe_make_parens_invisible_in_atom( - child, - parent=node, - remove_brackets_around_comma=False, - ): + # Remove redundant brackets around return type annotation. + is_return_annotation = False + for child in node.children: + if child.type == token.RARROW: + is_return_annotation = True + elif is_return_annotation: + if child.type == syms.atom and child.children[0].type == token.LPAR: + if maybe_make_parens_invisible_in_atom( + child, + parent=node, + remove_brackets_around_comma=False, + ): + wrap_in_parentheses(node, child, visible=False) + else: wrap_in_parentheses(node, child, visible=False) - else: - wrap_in_parentheses(node, child, visible=False) - is_return_annotation = False + is_return_annotation = False - for child in node.children: - yield from self.visit(child) + for child in node.children: + yield from self.visit(child) def visit_match_case(self, node: Node) -> Iterator[Line]: """Visit either a match or case statement.""" diff --git a/src/black/mode.py b/src/black/mode.py index 8510b94fd98..138ce11a9be 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -149,6 +149,7 @@ def supports_feature(target_versions: Set[TargetVersion], feature: Feature) -> b class Preview(Enum): """Individual preview style features.""" + annotation_parens = auto() long_docstring_quotes_on_newline = auto() string_processing = auto() From 42624f44e93bb51ca257c51f06933b8ac0f852f7 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 17 Dec 2022 09:45:35 -0800 Subject: [PATCH 17/32] fixup after merge --- src/black/comments.py | 4 +--- src/black/mode.py | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/black/comments.py b/src/black/comments.py index eb53ff86621..7cf15bf67b3 100644 --- a/src/black/comments.py +++ b/src/black/comments.py @@ -238,9 +238,7 @@ def generate_ignored_nodes(leaf: Leaf, comment: ProtoComment) -> Iterator[LN]: if ( child.type == token.INDENT and index < len(container.children) - 1 - and children_contains_fmt_on( - container.children[index + 1] - ) + and children_contains_fmt_on(container.children[index + 1]) ): # This means `# fmt: on` is placed right after an indentation # level, and we shouldn't swallow the previous INDENT token. diff --git a/src/black/mode.py b/src/black/mode.py index 163a42d0908..064544098b5 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -154,7 +154,6 @@ class Preview(Enum): # NOTE: string_processing requires wrap_long_dict_values_in_parens # for https://github.com/psf/black/issues/3117 to be fixed. string_processing = auto() - skip_magic_trailing_comma_in_subscript = auto() wrap_long_dict_values_in_parens = auto() From bce883b4d81d18b2c8e99690c5cddddbaf44162b Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 17 Dec 2022 09:46:38 -0800 Subject: [PATCH 18/32] enable long_docstring_quotes_on_newline --- src/black/linegen.py | 2 +- src/black/mode.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 0c5dcf2c0a9..fb8aa948f71 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -396,7 +396,7 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: quote = quote_char * quote_len # It's invalid to put closing single-character quotes on a new line. - if Preview.long_docstring_quotes_on_newline in self.mode and quote_len == 3: + if quote_len == 3: # We need to find the length of the last line of the docstring # to find if we can add the closing quotes to the line without # exceeding the maximum line length. diff --git a/src/black/mode.py b/src/black/mode.py index 064544098b5..7a220538ee0 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -150,7 +150,6 @@ class Preview(Enum): """Individual preview style features.""" annotation_parens = auto() - long_docstring_quotes_on_newline = auto() # NOTE: string_processing requires wrap_long_dict_values_in_parens # for https://github.com/psf/black/issues/3117 to be fixed. string_processing = auto() From d0b42a77b73dbb24eb37312ef5a84bed15e93bd4 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 17 Dec 2022 09:49:44 -0800 Subject: [PATCH 19/32] put prefer_splitting_right_hand_side_of_assignments back --- src/black/mode.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/black/mode.py b/src/black/mode.py index 7a220538ee0..125970e9767 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -150,6 +150,7 @@ class Preview(Enum): """Individual preview style features.""" annotation_parens = auto() + prefer_splitting_right_hand_side_of_assignments = auto() # NOTE: string_processing requires wrap_long_dict_values_in_parens # for https://github.com/psf/black/issues/3117 to be fixed. string_processing = auto() From c5fc62d37146525d3ef1d3451b9fa618ea5b798a Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 17 Dec 2022 19:04:04 -0800 Subject: [PATCH 20/32] add annotation_parens --- src/black/linegen.py | 39 ++++++++++++++++++--------------------- src/black/mode.py | 1 - 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 2a224b14e18..29bd57ec2db 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -200,30 +200,27 @@ def visit_dictsetmaker(self, node: Node) -> Iterator[Line]: def visit_funcdef(self, node: Node) -> Iterator[Line]: """Visit function definition.""" - if Preview.annotation_parens not in self.mode: - yield from self.visit_stmt(node, keywords={"def"}, parens=set()) - else: - yield from self.line() + yield from self.line() - # Remove redundant brackets around return type annotation. - is_return_annotation = False - for child in node.children: - if child.type == token.RARROW: - is_return_annotation = True - elif is_return_annotation: - if child.type == syms.atom and child.children[0].type == token.LPAR: - if maybe_make_parens_invisible_in_atom( - child, - parent=node, - remove_brackets_around_comma=False, - ): - wrap_in_parentheses(node, child, visible=False) - else: + # Remove redundant brackets around return type annotation. + is_return_annotation = False + for child in node.children: + if child.type == token.RARROW: + is_return_annotation = True + elif is_return_annotation: + if child.type == syms.atom and child.children[0].type == token.LPAR: + if maybe_make_parens_invisible_in_atom( + child, + parent=node, + remove_brackets_around_comma=False, + ): wrap_in_parentheses(node, child, visible=False) - is_return_annotation = False + else: + wrap_in_parentheses(node, child, visible=False) + is_return_annotation = False - for child in node.children: - yield from self.visit(child) + for child in node.children: + yield from self.visit(child) def visit_match_case(self, node: Node) -> Iterator[Line]: """Visit either a match or case statement.""" diff --git a/src/black/mode.py b/src/black/mode.py index 125970e9767..afa403e73c2 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -149,7 +149,6 @@ def supports_feature(target_versions: Set[TargetVersion], feature: Feature) -> b class Preview(Enum): """Individual preview style features.""" - annotation_parens = auto() prefer_splitting_right_hand_side_of_assignments = auto() # NOTE: string_processing requires wrap_long_dict_values_in_parens # for https://github.com/psf/black/issues/3117 to be fixed. From 0528f0734499b17c15ad34d30825d74e543c512c Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 17 Dec 2022 19:12:59 -0800 Subject: [PATCH 21/32] move some tests out of preview --- tests/data/{preview_310 => py_310}/remove_newline_after_match.py | 0 tests/data/{preview_39 => py_39}/remove_with_brackets.py | 0 tests/data/{preview => simple_cases}/whitespace.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename tests/data/{preview_310 => py_310}/remove_newline_after_match.py (100%) rename tests/data/{preview_39 => py_39}/remove_with_brackets.py (100%) rename tests/data/{preview => simple_cases}/whitespace.py (100%) diff --git a/tests/data/preview_310/remove_newline_after_match.py b/tests/data/py_310/remove_newline_after_match.py similarity index 100% rename from tests/data/preview_310/remove_newline_after_match.py rename to tests/data/py_310/remove_newline_after_match.py diff --git a/tests/data/preview_39/remove_with_brackets.py b/tests/data/py_39/remove_with_brackets.py similarity index 100% rename from tests/data/preview_39/remove_with_brackets.py rename to tests/data/py_39/remove_with_brackets.py diff --git a/tests/data/preview/whitespace.py b/tests/data/simple_cases/whitespace.py similarity index 100% rename from tests/data/preview/whitespace.py rename to tests/data/simple_cases/whitespace.py From 4842546862cc0613067ec7f88ba4155fbd28982a Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 17 Dec 2022 19:18:39 -0800 Subject: [PATCH 22/32] fix a test --- tests/data/simple_cases/fmtonoff.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/data/simple_cases/fmtonoff.py b/tests/data/simple_cases/fmtonoff.py index 5a50eb12ed3..e40ea2c8d21 100644 --- a/tests/data/simple_cases/fmtonoff.py +++ b/tests/data/simple_cases/fmtonoff.py @@ -205,6 +205,7 @@ def single_literal_yapf_disable(): # Comment 2 + # fmt: off def func_no_args(): a; b; c From 4acf3358a1757b299a471603d2d4d551019de76a Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 17 Dec 2022 19:19:34 -0800 Subject: [PATCH 23/32] Revert "enable long_docstring_quotes_on_newline" This reverts commit bce883b4d81d18b2c8e99690c5cddddbaf44162b. --- src/black/linegen.py | 2 +- src/black/mode.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 29bd57ec2db..64e9b91405b 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -393,7 +393,7 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: quote = quote_char * quote_len # It's invalid to put closing single-character quotes on a new line. - if quote_len == 3: + if Preview.long_docstring_quotes_on_newline in self.mode and quote_len == 3: # We need to find the length of the last line of the docstring # to find if we can add the closing quotes to the line without # exceeding the maximum line length. diff --git a/src/black/mode.py b/src/black/mode.py index afa403e73c2..4148266857e 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -150,6 +150,7 @@ class Preview(Enum): """Individual preview style features.""" prefer_splitting_right_hand_side_of_assignments = auto() + long_docstring_quotes_on_newline = auto() # NOTE: string_processing requires wrap_long_dict_values_in_parens # for https://github.com/psf/black/issues/3117 to be fixed. string_processing = auto() From d48f8ed854d585ca0fac73c4785bdc93a81471ad Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 17 Dec 2022 19:22:08 -0800 Subject: [PATCH 24/32] fix line-length 1 bug --- src/black/linegen.py | 3 ++- src/black/mode.py | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 64e9b91405b..33257fccc54 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -393,7 +393,7 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: quote = quote_char * quote_len # It's invalid to put closing single-character quotes on a new line. - if Preview.long_docstring_quotes_on_newline in self.mode and quote_len == 3: + if self.mode and quote_len == 3: # We need to find the length of the last line of the docstring # to find if we can add the closing quotes to the line without # exceeding the maximum line length. @@ -408,6 +408,7 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: if ( len(lines) > 1 and last_line_length + quote_len > self.mode.line_length + and len(indent) + quote_len <= self.mode.line_length ): leaf.value = prefix + quote + docstring + "\n" + indent + quote else: diff --git a/src/black/mode.py b/src/black/mode.py index 4148266857e..afa403e73c2 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -150,7 +150,6 @@ class Preview(Enum): """Individual preview style features.""" prefer_splitting_right_hand_side_of_assignments = auto() - long_docstring_quotes_on_newline = auto() # NOTE: string_processing requires wrap_long_dict_values_in_parens # for https://github.com/psf/black/issues/3117 to be fixed. string_processing = auto() From 387f3fbaaa4f1c260bdba26b9b9990b39a2c9142 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 17 Dec 2022 19:31:38 -0800 Subject: [PATCH 25/32] fix tests --- .../py_310/parenthesized_context_managers.py | 24 +++++++++++++++++++ tests/data/py_311/pep_654_style.py | 2 +- tests/data/simple_cases/comments2.py | 1 + tests/data/simple_cases/comments3.py | 5 ++-- tests/data/simple_cases/comments5.py | 2 ++ tests/data/simple_cases/empty_lines.py | 1 - .../simple_cases/function_trailing_comma.py | 16 +++++-------- 7 files changed, 37 insertions(+), 14 deletions(-) diff --git a/tests/data/py_310/parenthesized_context_managers.py b/tests/data/py_310/parenthesized_context_managers.py index ccf1f94883e..1ef09a1bd34 100644 --- a/tests/data/py_310/parenthesized_context_managers.py +++ b/tests/data/py_310/parenthesized_context_managers.py @@ -19,3 +19,27 @@ CtxManager3() as example3, ): ... + +# output + +with CtxManager() as example: + ... + +with CtxManager1(), CtxManager2(): + ... + +with CtxManager1() as example, CtxManager2(): + ... + +with CtxManager1(), CtxManager2() as example: + ... + +with CtxManager1() as example1, CtxManager2() as example2: + ... + +with ( + CtxManager1() as example1, + CtxManager2() as example2, + CtxManager3() as example3, +): + ... diff --git a/tests/data/py_311/pep_654_style.py b/tests/data/py_311/pep_654_style.py index 568e5e3efa4..9fc7c0c84db 100644 --- a/tests/data/py_311/pep_654_style.py +++ b/tests/data/py_311/pep_654_style.py @@ -76,7 +76,7 @@ except: try: raise TypeError(int) - except* (Exception): + except* Exception: pass 1 / 0 except Exception as e: diff --git a/tests/data/simple_cases/comments2.py b/tests/data/simple_cases/comments2.py index 4eea013151a..37e185abf4f 100644 --- a/tests/data/simple_cases/comments2.py +++ b/tests/data/simple_cases/comments2.py @@ -226,6 +226,7 @@ def _init_host(self, parsed) -> None: add_compiler(compilers[(7.0, 32)]) # add_compiler(compilers[(7.1, 64)]) + # Comment before function. def inline_comments_in_brackets_ruin_everything(): if typedargslist: diff --git a/tests/data/simple_cases/comments3.py b/tests/data/simple_cases/comments3.py index fbbef6dcc6b..f964bee6651 100644 --- a/tests/data/simple_cases/comments3.py +++ b/tests/data/simple_cases/comments3.py @@ -1,6 +1,7 @@ # The percent-percent comments are Spyder IDE cells. -#%% + +# %% def func(): x = """ a really long string @@ -44,4 +45,4 @@ def func(): ) -#%% \ No newline at end of file +# %% \ No newline at end of file diff --git a/tests/data/simple_cases/comments5.py b/tests/data/simple_cases/comments5.py index c8c38813d55..bda40619f62 100644 --- a/tests/data/simple_cases/comments5.py +++ b/tests/data/simple_cases/comments5.py @@ -62,6 +62,8 @@ def decorated1(): # Preview.empty_lines_before_class_or_def_with_leading_comments. # In the current style, the user will have to split those lines by hand. some_instruction + + # This comment should be split from `some_instruction` by two lines but isn't. def g(): ... diff --git a/tests/data/simple_cases/empty_lines.py b/tests/data/simple_cases/empty_lines.py index 4c03e432383..4fd47b93dca 100644 --- a/tests/data/simple_cases/empty_lines.py +++ b/tests/data/simple_cases/empty_lines.py @@ -119,7 +119,6 @@ def f(): if not prev: prevp = preceding_leaf(p) if not prevp or prevp.type in OPENING_BRACKETS: - return NO if prevp.type == token.EQUAL: diff --git a/tests/data/simple_cases/function_trailing_comma.py b/tests/data/simple_cases/function_trailing_comma.py index abe9617c0e9..92f46e27516 100644 --- a/tests/data/simple_cases/function_trailing_comma.py +++ b/tests/data/simple_cases/function_trailing_comma.py @@ -116,9 +116,9 @@ def f( pass -def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -]: +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + Set["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] +): json = { "k": { "k2": { @@ -140,9 +140,7 @@ def some_function_with_a_really_long_name() -> ( def some_method_with_a_really_long_name( very_long_parameter_so_yeah: str, another_long_parameter: int -) -> ( - another_case_of_returning_a_deeply_nested_import_of_a_type_i_suppose_cause_why_not -): +) -> another_case_of_returning_a_deeply_nested_import_of_a_type_i_suppose_cause_why_not: pass @@ -155,10 +153,8 @@ def func() -> ( def func() -> ( - ( - also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( - this_shouldn_t_get_a_trailing_comma_too - ) + also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( + this_shouldn_t_get_a_trailing_comma_too ) ): pass From c9280bf91628901c58672c68e7617a2251ac03a7 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 17 Dec 2022 19:41:45 -0800 Subject: [PATCH 26/32] move more tests to non-preview --- .../preview/return_annotation_brackets_string.py | 12 ++++++++++++ tests/data/{preview => simple_cases}/comments8.py | 0 tests/data/{preview => simple_cases}/comments9.py | 0 .../{preview => simple_cases}/docstring_preview.py | 0 .../one_element_subscript.py | 0 .../prefer_rhs_split_reformatted.py | 0 .../{preview => simple_cases}/remove_await_parens.py | 0 .../remove_except_parens.py | 0 .../{preview => simple_cases}/remove_for_brackets.py | 0 .../remove_newline_after_code_block_open.py | 0 .../return_annotation_brackets.py | 12 ------------ .../skip_magic_trailing_comma.py | 0 .../trailing_commas_in_leading_parts.py | 0 tests/test_format.py | 12 +++++------- 14 files changed, 17 insertions(+), 19 deletions(-) create mode 100644 tests/data/preview/return_annotation_brackets_string.py rename tests/data/{preview => simple_cases}/comments8.py (100%) rename tests/data/{preview => simple_cases}/comments9.py (100%) rename tests/data/{preview => simple_cases}/docstring_preview.py (100%) rename tests/data/{preview => simple_cases}/one_element_subscript.py (100%) rename tests/data/{preview => simple_cases}/prefer_rhs_split_reformatted.py (100%) rename tests/data/{preview => simple_cases}/remove_await_parens.py (100%) rename tests/data/{preview => simple_cases}/remove_except_parens.py (100%) rename tests/data/{preview => simple_cases}/remove_for_brackets.py (100%) rename tests/data/{preview => simple_cases}/remove_newline_after_code_block_open.py (100%) rename tests/data/{preview => simple_cases}/return_annotation_brackets.py (93%) rename tests/data/{preview => simple_cases}/skip_magic_trailing_comma.py (100%) rename tests/data/{preview => simple_cases}/trailing_commas_in_leading_parts.py (100%) diff --git a/tests/data/preview/return_annotation_brackets_string.py b/tests/data/preview/return_annotation_brackets_string.py new file mode 100644 index 00000000000..6978829fd5c --- /dev/null +++ b/tests/data/preview/return_annotation_brackets_string.py @@ -0,0 +1,12 @@ +# Long string example +def frobnicate() -> "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]": + pass + +# output + +# Long string example +def frobnicate() -> ( + "ThisIsTrulyUnreasonablyExtremelyLongClassName |" + " list[ThisIsTrulyUnreasonablyExtremelyLongClassName]" +): + pass diff --git a/tests/data/preview/comments8.py b/tests/data/simple_cases/comments8.py similarity index 100% rename from tests/data/preview/comments8.py rename to tests/data/simple_cases/comments8.py diff --git a/tests/data/preview/comments9.py b/tests/data/simple_cases/comments9.py similarity index 100% rename from tests/data/preview/comments9.py rename to tests/data/simple_cases/comments9.py diff --git a/tests/data/preview/docstring_preview.py b/tests/data/simple_cases/docstring_preview.py similarity index 100% rename from tests/data/preview/docstring_preview.py rename to tests/data/simple_cases/docstring_preview.py diff --git a/tests/data/preview/one_element_subscript.py b/tests/data/simple_cases/one_element_subscript.py similarity index 100% rename from tests/data/preview/one_element_subscript.py rename to tests/data/simple_cases/one_element_subscript.py diff --git a/tests/data/preview/prefer_rhs_split_reformatted.py b/tests/data/simple_cases/prefer_rhs_split_reformatted.py similarity index 100% rename from tests/data/preview/prefer_rhs_split_reformatted.py rename to tests/data/simple_cases/prefer_rhs_split_reformatted.py diff --git a/tests/data/preview/remove_await_parens.py b/tests/data/simple_cases/remove_await_parens.py similarity index 100% rename from tests/data/preview/remove_await_parens.py rename to tests/data/simple_cases/remove_await_parens.py diff --git a/tests/data/preview/remove_except_parens.py b/tests/data/simple_cases/remove_except_parens.py similarity index 100% rename from tests/data/preview/remove_except_parens.py rename to tests/data/simple_cases/remove_except_parens.py diff --git a/tests/data/preview/remove_for_brackets.py b/tests/data/simple_cases/remove_for_brackets.py similarity index 100% rename from tests/data/preview/remove_for_brackets.py rename to tests/data/simple_cases/remove_for_brackets.py diff --git a/tests/data/preview/remove_newline_after_code_block_open.py b/tests/data/simple_cases/remove_newline_after_code_block_open.py similarity index 100% rename from tests/data/preview/remove_newline_after_code_block_open.py rename to tests/data/simple_cases/remove_newline_after_code_block_open.py diff --git a/tests/data/preview/return_annotation_brackets.py b/tests/data/simple_cases/return_annotation_brackets.py similarity index 93% rename from tests/data/preview/return_annotation_brackets.py rename to tests/data/simple_cases/return_annotation_brackets.py index 27760bd51d7..265c30220d8 100644 --- a/tests/data/preview/return_annotation_brackets.py +++ b/tests/data/simple_cases/return_annotation_brackets.py @@ -87,10 +87,6 @@ def foo() -> tuple[loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo def foo() -> tuple[int, int, int,]: return 2 -# Long string example -def frobnicate() -> "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]": - pass - # output # Control def double(a: int) -> int: @@ -212,11 +208,3 @@ def foo() -> ( ] ): return 2 - - -# Long string example -def frobnicate() -> ( - "ThisIsTrulyUnreasonablyExtremelyLongClassName |" - " list[ThisIsTrulyUnreasonablyExtremelyLongClassName]" -): - pass diff --git a/tests/data/preview/skip_magic_trailing_comma.py b/tests/data/simple_cases/skip_magic_trailing_comma.py similarity index 100% rename from tests/data/preview/skip_magic_trailing_comma.py rename to tests/data/simple_cases/skip_magic_trailing_comma.py diff --git a/tests/data/preview/trailing_commas_in_leading_parts.py b/tests/data/simple_cases/trailing_commas_in_leading_parts.py similarity index 100% rename from tests/data/preview/trailing_commas_in_leading_parts.py rename to tests/data/simple_cases/trailing_commas_in_leading_parts.py diff --git a/tests/test_format.py b/tests/test_format.py index 01cd61eef63..feb3cba956b 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -31,17 +31,15 @@ def check_file( @pytest.mark.filterwarnings("ignore:invalid escape sequence.*:DeprecationWarning") @pytest.mark.parametrize("filename", all_data_cases("simple_cases")) def test_simple_format(filename: str) -> None: - check_file("simple_cases", filename, DEFAULT_MODE) + magic_trailing_comma = filename != "skip_magic_trailing_comma" + check_file( + "simple_cases", filename, black.Mode(magic_trailing_comma=magic_trailing_comma) + ) @pytest.mark.parametrize("filename", all_data_cases("preview")) def test_preview_format(filename: str) -> None: - magic_trailing_comma = filename != "skip_magic_trailing_comma" - check_file( - "preview", - filename, - black.Mode(preview=True, magic_trailing_comma=magic_trailing_comma), - ) + check_file("preview", filename, black.Mode(preview=True)) @pytest.mark.parametrize("filename", all_data_cases("preview_39")) From 88957e5a2456b538ae9c4a136a8fdf3fb20160e6 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 17 Dec 2022 19:46:49 -0800 Subject: [PATCH 27/32] changelog entry --- CHANGES.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index f786f1a1fed..b5cac726175 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,35 @@ +- Introduce the 2023 stable style, which incorporates most aspects of last year's + preview style. Specific changes: + - Enforce empty lines before classes and functions with sticky leading comments + (#3302) (22.12.0) + - Reformat empty and whitespace-only files as either an empty file (if no newline is + present) or as a single newline character (if a newline is present) (#3348) + (22.12.0) + - Implicitly concatenated strings used as function args are now wrapped inside + parentheses (#3307) (22.12.0) + - Correctly handle trailing commas that are inside a line's leading non-nested parens + (#3370) (22.12.0) + - `--skip-string-normalization` / `-S` now prevents docstring prefixes from being + normalized as expected (#3168) (since 22.8.0) + - When using `--skip-magic-trailing-comma` or `-C`, trailing commas are stripped from + subscript expressions with more than 1 element (#3209) (22.8.0) + - Implicitly concatenated strings inside a list, set, or tuple are now wrapped inside + parentheses (#3162) (22.8.0) + - Fix a string merging/split issue when a comment is present in the middle of + implicitly concatenated strings on its own line (#3227) (22.8.0) + - Docstring quotes are no longer moved if it would violate the line length limit + (#3044, #3430) (22.6.0) + - Parentheses around return annotations are now managed (#2990) (22.6.0) + - Remove unnecessary parentheses around awaited objects (#2991) (22.6.0) + - Remove unnecessary parentheses in `with` statements (#2926) (22.6.0) + - Remove trailing newlines after code block open (#3035) (22.6.0) + - Code cell separators `#%%` are now standardised to `# %%` (#2919) (22.3.0) + - Remove unnecessary parentheses from `except` statements (#2939) (22.3.0) + - Remove unnecessary parentheses from tuple unpacking in `for` loops (#2945) (22.3.0) + - Avoid magic-trailing-comma in single-element subscripts (#2942) (22.3.0) - Fix a crash when a colon line is marked between `# fmt: off` and `# fmt: on` (#3439) ### Preview style From a5bc39981275c3d54794edb0c5708e6eed0ee519 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 17 Dec 2022 19:48:10 -0800 Subject: [PATCH 28/32] delete preview_39/310 --- tests/test_format.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/tests/test_format.py b/tests/test_format.py index feb3cba956b..b5da0677211 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -42,20 +42,6 @@ def test_preview_format(filename: str) -> None: check_file("preview", filename, black.Mode(preview=True)) -@pytest.mark.parametrize("filename", all_data_cases("preview_39")) -def test_preview_minimum_python_39_format(filename: str) -> None: - source, expected = read_data("preview_39", filename) - mode = black.Mode(preview=True) - assert_format(source, expected, mode, minimum_version=(3, 9)) - - -@pytest.mark.parametrize("filename", all_data_cases("preview_310")) -def test_preview_minimum_python_310_format(filename: str) -> None: - source, expected = read_data("preview_310", filename) - mode = black.Mode(preview=True) - assert_format(source, expected, mode, minimum_version=(3, 10)) - - # =============== # # Complex cases # ============= # From 8aa39b69fca3d78baf841fc4bb2f4202936a67e1 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 17 Dec 2022 19:49:12 -0800 Subject: [PATCH 29/32] fix changelog --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index b5cac726175..92b30f067c1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,7 +11,7 @@ - Introduce the 2023 stable style, which incorporates most aspects of last year's - preview style. Specific changes: + preview style (#3418). Specific changes: - Enforce empty lines before classes and functions with sticky leading comments (#3302) (22.12.0) - Reformat empty and whitespace-only files as either an empty file (if no newline is From 1be7214cb58db2d4324e058f0d0d2ee019cbef0d Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 30 Jan 2023 17:23:35 -0800 Subject: [PATCH 30/32] fix up merge --- src/black/__init__.py | 9 +++++++-- src/black/mode.py | 6 ------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/black/__init__.py b/src/black/__init__.py index 0496ad58997..42bdfc1a5dd 100644 --- a/src/black/__init__.py +++ b/src/black/__init__.py @@ -1093,8 +1093,13 @@ def _format_str_once(src_contents: str, *, mode: Mode) -> str: future_imports = get_future_imports(src_node) versions = detect_target_versions(src_node, future_imports=future_imports) - normalize_fmt_off(src_node, preview=mode.preview) - lines = LineGenerator(mode=mode) + context_manager_features = { + feature + for feature in {Feature.PARENTHESIZED_CONTEXT_MANAGERS} + if supports_feature(versions, feature) + } + normalize_fmt_off(src_node) + lines = LineGenerator(mode=mode, features=context_manager_features) elt = EmptyLineTracker(mode=mode) split_line_features = { feature diff --git a/src/black/mode.py b/src/black/mode.py index 93508dbccb6..1af16070073 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -154,12 +154,6 @@ class Preview(Enum): """Individual preview style features.""" hex_codes_in_unicode_sequences = auto() - annotation_parens = auto() - empty_lines_before_class_or_def_with_leading_comments = auto() - handle_trailing_commas_in_head = auto() - long_docstring_quotes_on_newline = auto() - normalize_docstring_quotes_and_prefixes_properly = auto() - one_element_subscript = auto() prefer_splitting_right_hand_side_of_assignments = auto() # NOTE: string_processing requires wrap_long_dict_values_in_parens # for https://github.com/psf/black/issues/3117 to be fixed. From 2c0b3336fc95f3347867ee9c095e429a788eaa60 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 30 Jan 2023 17:29:21 -0800 Subject: [PATCH 31/32] now we do remove them --- tests/data/py_38/pep_572_remove_parens.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/data/py_38/pep_572_remove_parens.py b/tests/data/py_38/pep_572_remove_parens.py index 4e95fb07f3a..b952b2940c5 100644 --- a/tests/data/py_38/pep_572_remove_parens.py +++ b/tests/data/py_38/pep_572_remove_parens.py @@ -62,7 +62,6 @@ async def await_the_walrus(): with (x := await a, y := await b): pass - # Ideally we should remove one set of parentheses with ((x := await a, y := await b)): pass @@ -137,8 +136,7 @@ async def await_the_walrus(): with (x := await a, y := await b): pass - # Ideally we should remove one set of parentheses - with ((x := await a, y := await b)): + with (x := await a, y := await b): pass with (x := await a), (y := await b): From 1c8d15749da9fb4a760289a7b8e3e559e84858c5 Mon Sep 17 00:00:00 2001 From: Richard Si Date: Tue, 31 Jan 2023 15:23:30 -0500 Subject: [PATCH 32/32] Update style docs --- docs/the_black_code_style/current_style.md | 40 +++++++++- docs/the_black_code_style/future_style.md | 90 ---------------------- 2 files changed, 39 insertions(+), 91 deletions(-) diff --git a/docs/the_black_code_style/current_style.md b/docs/the_black_code_style/current_style.md index 56b92529d70..83f8785cc55 100644 --- a/docs/the_black_code_style/current_style.md +++ b/docs/the_black_code_style/current_style.md @@ -194,7 +194,45 @@ that in-function vertical whitespace should only be used sparingly. _Black_ will allow single empty lines inside functions, and single and double empty lines on module level left by the original editors, except when they're within parenthesized expressions. Since such expressions are always reformatted to fit minimal -space, this whitespace is lost. +space, this whitespace is lost. The other exception is that it will remove any empty +lines immediately following a statement that introduces a new indentation level. + +```python +# in: + +def foo(): + + print("All the newlines above me should be deleted!") + + +if condition: + + print("No newline above me!") + + print("There is a newline above me, and that's OK!") + + +class Point: + + x: int + y: int + +# out: + +def foo(): + print("All the newlines above me should be deleted!") + + +if condition: + print("No newline above me!") + + print("There is a newline above me, and that's OK!") + + +class Point: + x: int + y: int +``` It will also insert proper spacing before and after function definitions. It's one line before and after inner functions and two lines before and after module-level functions diff --git a/docs/the_black_code_style/future_style.md b/docs/the_black_code_style/future_style.md index 7a0b2d8f07a..6d289d460a7 100644 --- a/docs/the_black_code_style/future_style.md +++ b/docs/the_black_code_style/future_style.md @@ -62,93 +62,3 @@ plain strings. User-made splits are respected when they do not exceed the line l limit. Line continuation backslashes are converted into parenthesized strings. Unnecessary parentheses are stripped. The stability and status of this feature is tracked in [this issue](https://github.com/psf/black/issues/2188). - -### Improved empty line management - -1. _Black_ will remove newlines in the beginning of new code blocks, i.e. when the - indentation level is increased. For example: - - ```python - def my_func(): - - print("The line above me will be deleted!") - ``` - - will be changed to: - - ```python - def my_func(): - print("The line above me will be deleted!") - ``` - - This new feature will be applied to **all code blocks**: `def`, `class`, `if`, - `for`, `while`, `with`, `case` and `match`. - -2. _Black_ will enforce empty lines before classes and functions with leading comments. - For example: - - ```python - some_var = 1 - # Leading sticky comment - def my_func(): - ... - ``` - - will be changed to: - - ```python - some_var = 1 - - - # Leading sticky comment - def my_func(): - ... - ``` - -### Improved parentheses management - -_Black_ will format parentheses around return annotations similarly to other sets of -parentheses. For example: - -```python -def foo() -> (int): - ... - -def foo() -> looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong: - ... -``` - -will be changed to: - -```python -def foo() -> int: - ... - - -def foo() -> ( - looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong -): - ... -``` - -And, extra parentheses in `await` expressions and `with` statements are removed. For -example: - -```python -with ((open("bla.txt")) as f, open("x")): - ... - -async def main(): - await (asyncio.sleep(1)) -``` - -will be changed to: - -```python -with open("bla.txt") as f, open("x"): - ... - - -async def main(): - await asyncio.sleep(1) -```