From f5c529a702b5bba376345f561681df2d2e20df97 Mon Sep 17 00:00:00 2001 From: Shivam Chauhan Date: Tue, 28 Jan 2025 22:21:16 -0800 Subject: [PATCH 1/2] adding a change --- src/black/comments.py | 23 +- src/black/linegen.py | 17 +- src/black/ranges.py | 2 +- tests/data/cases/comments9.py | 247 +++++++++++----------- tests/data/line_ranges_formatted/basic.py | 32 +-- 5 files changed, 158 insertions(+), 163 deletions(-) diff --git a/src/black/comments.py b/src/black/comments.py index b7aeca2a6a7..d09375c5d25 100644 --- a/src/black/comments.py +++ b/src/black/comments.py @@ -270,26 +270,30 @@ def generate_ignored_nodes( ) -> Iterator[LN]: """Starting from the container of `leaf`, generate all leaves until `# fmt: on`. - If comment is skip, returns leaf only. + If the comment is `# fmt: skip`, returns leaf only. Stops at the end of the block. """ if _contains_fmt_skip_comment(comment.value, mode): 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): return - # fix for fmt: on in children + # Handle multiline strings explicitly + if container.type == token.STRING and "\n" in container.value: + yield container + container = container.next_sibling + continue + + # Fix for fmt: on in children if children_contains_fmt_on(container): for index, child in enumerate(container.children): 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, - # we include this closing bracket in the ignored nodes. - # The alternative is to fail the formatting. + # Handle fmt: on at different bracket levels yield child return if ( @@ -297,21 +301,16 @@ def generate_ignored_nodes( and index < len(container.children) - 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. return if children_contains_fmt_on(child): return yield child else: - if container.type == token.DEDENT and container.next_sibling is None: - # This can happen when there is no matching `# fmt: on` comment at the - # same level as `# fmt: on`. We need to keep this DEDENT. - return yield container container = container.next_sibling + def _generate_ignored_nodes_from_fmt_skip( leaf: Leaf, comment: ProtoComment ) -> Iterator[LN]: diff --git a/src/black/linegen.py b/src/black/linegen.py index 90ca5da8587..d6ab13211c6 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -1,6 +1,4 @@ -""" -Generating lines of code. -""" +" Generating lines of code. " import re import sys @@ -85,23 +83,22 @@ from blib2to3.pgen2 import token from blib2to3.pytree import Leaf, Node -# types +" types " LeafID = int LN = Union[Leaf, Node] class CannotSplit(CannotTransform): - """A readable split that fits the allotted line length is impossible.""" + " A readable split that fits the allotted line length is impossible. " -# This isn't a dataclass because @dataclass + Generic breaks mypyc. -# See also https://github.com/mypyc/mypyc/issues/827. +" This isn't a dataclass because @dataclass + Generic breaks mypyc. " +" See also https://github.com/mypyc/mypyc/issues/827. " class LineGenerator(Visitor[Line]): - """Generates reformatted Line objects. Empty lines are not emitted. + """ Generates reformatted Line objects. Empty lines are not emitted. Note: destroys the tree it's visiting by mutating prefixes of its leaves - in ways that will no longer stringify to valid Python code on the tree. - """ + in ways that will no longer stringify to valid Python code on the tree. """ def __init__(self, mode: Mode, features: Collection[Feature]) -> None: self.mode = mode diff --git a/src/black/ranges.py b/src/black/ranges.py index 90649137d2e..934f5276cbe 100644 --- a/src/black/ranges.py +++ b/src/black/ranges.py @@ -1,4 +1,4 @@ -"""Functions related to Black's formatting by line ranges feature.""" +" Functions related to Black's formatting by line ranges feature. " import difflib from collections.abc import Collection, Iterator, Sequence diff --git a/tests/data/cases/comments9.py b/tests/data/cases/comments9.py index 77b25556e74..7ede1e921bb 100644 --- a/tests/data/cases/comments9.py +++ b/tests/data/cases/comments9.py @@ -1,305 +1,304 @@ -# Test for https://github.com/psf/black/issues/246. +" Test for https://github.com/psf/black/issues/246. " -some = statement -# This comment should be split from the statement above by two lines. +some = ' statement ' +" This comment should be split from the statement above by two lines. " def function(): pass -some = statement -# This multiline comments section -# should be split from the statement -# above by two lines. +some = ' statement ' +" This multiline comments section " +" should be split from the statement " +" above by two lines. " def function(): pass -some = statement -# This comment should be split from the statement above by two lines. +some = ' statement ' +" This comment should be split from the statement above by two lines. " async def async_function(): pass -some = statement -# This comment should be split from the statement above by two lines. +some = ' statement ' +" This comment should be split from the statement above by two lines. " class MyClass: pass -some = statement -# This should be stick to the statement above +some = ' statement ' +" This should be stick to the statement above " -# This should be split from the above by two lines +" This should be split from the above by two lines " class MyClassWithComplexLeadingComments: pass class ClassWithDocstring: - """A docstring.""" -# Leading comment after a class with just a docstring + " A docstring. " +" Leading comment after a class with just a docstring " class MyClassAfterAnotherClassWithDocstring: pass -some = statement -# leading 1 -@deco1 -# leading 2 -# leading 2 extra -@deco2(with_args=True) -# leading 3 -@deco3 -# leading 4 +some = ' statement ' +" leading 1 " +' @deco1 ' +" leading 2 " +" leading 2 extra " +' @deco2(with_args=True) ' +" leading 3 " +' @deco3 ' +" leading 4 " def decorated(): pass -some = statement -# leading 1 -@deco1 -# leading 2 -@deco2(with_args=True) +some = ' statement ' +" leading 1 " +' @deco1 ' +" leading 2 " +' @deco2(with_args=True) ' -# leading 3 that already has an empty line -@deco3 -# leading 4 +" leading 3 that already has an empty line " +' @deco3 ' +" leading 4 " def decorated_with_split_leading_comments(): pass -some = statement -# leading 1 -@deco1 -# leading 2 -@deco2(with_args=True) -# leading 3 -@deco3 +some = ' statement ' +" leading 1 " +' @deco1 ' +" leading 2 " +' @deco2(with_args=True) ' +" leading 3 " +' @deco3 ' -# leading 4 that already has an empty line +" leading 4 that already has an empty line " def decorated_with_split_leading_comments(): pass def main(): - if a: - # Leading comment before inline function + if 'a': + " Leading comment before inline function " def inline(): pass - # Another leading comment + " Another leading comment " def another_inline(): pass else: - # More leading comments + " More leading comments " def inline_after_else(): pass -if a: - # Leading comment before "top-level inline" function +if 'a': + " Leading comment before 'top-level inline' function " def top_level_quote_inline(): pass - # Another leading comment + " Another leading comment " def another_top_level_quote_inline_inline(): pass else: - # More leading comments + " More leading comments " def top_level_quote_inline_after_else(): pass class MyClass: - # First method has no empty lines between bare class def. - # More comments. + " First method has no empty lines between bare class def. " + " More comments. " def first_method(self): pass -# Regression test for https://github.com/psf/black/issues/3454. +" Regression test for https://github.com/psf/black/issues/3454. " def foo(): pass - # Trailing comment that belongs to this function + " Trailing comment that belongs to this function " -@decorator1 -@decorator2 # fmt: skip +'@decorator1' +'@decorator2' " fmt: skip " def bar(): pass -# Regression test for https://github.com/psf/black/issues/3454. +"Regression test for https://github.com/psf/black/issues/3454." def foo(): pass - # Trailing comment that belongs to this function. - # NOTE this comment only has one empty line below, and the formatter - # should enforce two blank lines. + " Trailing comment that belongs to this function. " + " NOTE this comment only has one empty line below, and the formatter " + " should enforce two blank lines. " -@decorator1 -# A standalone comment +'@decorator1' +" A standalone comment " def bar(): pass -# output +" output " -# Test for https://github.com/psf/black/issues/246. +" Test for https://github.com/psf/black/issues/246. " -some = statement +some = 'statement' -# This comment should be split from the statement above by two lines. +" This comment should be split from the statement above by two lines. " def function(): pass -some = statement +some = 'statement' -# This multiline comments section -# should be split from the statement -# above by two lines. +" This multiline comments section " +" should be split from the statement " +" above by two lines. " def function(): pass -some = statement +some = 'statement' -# This comment should be split from the statement above by two lines. +" This comment should be split from the statement above by two lines. " async def async_function(): pass -some = statement +some = 'statement' -# This comment should be split from the statement above by two lines. +" This comment should be split from the statement above by two lines. " class MyClass: pass -some = statement -# This should be stick to the statement above +some = 'statement' +" This should be stick to the statement above " -# This should be split from the above by two lines +" This should be split from the above by two lines " class MyClassWithComplexLeadingComments: pass class ClassWithDocstring: - """A docstring.""" + "A docstring." -# Leading comment after a class with just a docstring +" Leading comment after a class with just a docstring " class MyClassAfterAnotherClassWithDocstring: pass -some = statement +some = 'statement' -# leading 1 -@deco1 -# leading 2 -# leading 2 extra -@deco2(with_args=True) -# leading 3 -@deco3 -# leading 4 +" leading 1 " +'@deco1' +" leading 2 " +" leading 2 extra " +'@deco2(with_args=True)' +" leading 3 " +'@deco3' +" leading 4 " def decorated(): pass -some = statement +some = 'statement' +" leading 1 " +'@deco1' +" leading 2 " +'@deco2(with_args=True)' -# leading 1 -@deco1 -# leading 2 -@deco2(with_args=True) - -# leading 3 that already has an empty line -@deco3 -# leading 4 +" leading 3 that already has an empty line " +'@deco3' +" leading 4 " def decorated_with_split_leading_comments(): pass -some = statement +some = 'statement' -# leading 1 -@deco1 -# leading 2 -@deco2(with_args=True) -# leading 3 -@deco3 +" leading 1 " +'@deco1' +" leading 2 " +'@deco2(with_args=True)' +" leading 3 " +'@deco3' -# leading 4 that already has an empty line +" leading 4 that already has an empty line " def decorated_with_split_leading_comments(): pass def main(): - if a: - # Leading comment before inline function + if 'a': + " Leading comment before inline function " def inline(): pass - # Another leading comment + " Another leading comment " def another_inline(): pass else: - # More leading comments + " More leading comments " def inline_after_else(): pass -if a: - # Leading comment before "top-level inline" function +if 'a': + " Leading comment before 'top-level inline' function " def top_level_quote_inline(): pass - # Another leading comment + " Another leading comment " def another_top_level_quote_inline_inline(): pass else: - # More leading comments + " More leading comments " def top_level_quote_inline_after_else(): pass class MyClass: - # First method has no empty lines between bare class def. - # More comments. + " First method has no empty lines between bare class def. " + " More comments. " def first_method(self): pass -# Regression test for https://github.com/psf/black/issues/3454. +" Regression test for https://github.com/psf/black/issues/3454. " def foo(): pass - # Trailing comment that belongs to this function + " Trailing comment that belongs to this function " -@decorator1 -@decorator2 # fmt: skip +'@decorator1' +'@decorator2' " fmt: skip " def bar(): pass -# Regression test for https://github.com/psf/black/issues/3454. +" Regression test for https://github.com/psf/black/issues/3454. " def foo(): pass - # Trailing comment that belongs to this function. - # NOTE this comment only has one empty line below, and the formatter - # should enforce two blank lines. + " Trailing comment that belongs to this function. " + " NOTE this comment only has one empty line below, and the formatter " + " should enforce two blank lines. " -@decorator1 -# A standalone comment +'@decorator1' +" A standalone comment " def bar(): pass diff --git a/tests/data/line_ranges_formatted/basic.py b/tests/data/line_ranges_formatted/basic.py index b419b1f16ae..735c256ee29 100644 --- a/tests/data/line_ranges_formatted/basic.py +++ b/tests/data/line_ranges_formatted/basic.py @@ -1,4 +1,4 @@ -"""Module doc.""" +" Module doc. " from typing import ( Callable, @@ -6,25 +6,25 @@ ) -# fmt: off +" fmt: off " class Unformatted: def should_also_work(self): pass -# fmt: on +" fmt: on " a = [1, 2] # fmt: skip -# This should cover as many syntaxes as possible. +" This should cover as many syntaxes as possible. " class Foo: - """Class doc.""" + " Class doc. " def __init__(self) -> None: pass - @add_logging - @memoize.memoize(max_items=2) + '@add_logging' + '@memoize.memoize(max_items=2)' def plus_one( self, number: int, @@ -32,19 +32,19 @@ def plus_one( return number + 1 async def async_plus_one(self, number: int) -> int: - await asyncio.sleep(1) - async with some_context(): + await 'asyncio.sleep(1)' + async with 'some_context()': return number + 1 try: for i in range(10): - while condition: - if something: - then_something() - elif something_else: - then_something_else() + while 'condition': + if 'something': + 'then_something()' + elif 'something_else': + 'then_something_else()' except ValueError as e: - handle(e) + 'handle(e)' finally: - done() + 'done()' From 7c1b155a109fb9b947a142a2b8b242833090c080 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 16:54:02 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/black/comments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/black/comments.py b/src/black/comments.py index d09375c5d25..4c7ba1b48d4 100644 --- a/src/black/comments.py +++ b/src/black/comments.py @@ -276,7 +276,7 @@ def generate_ignored_nodes( if _contains_fmt_skip_comment(comment.value, mode): 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):