From 63d892f1e408ae49901ac449235e95b57f2bfcf1 Mon Sep 17 00:00:00 2001 From: konstin Date: Thu, 1 Jun 2023 15:25:50 +0200 Subject: [PATCH] Implement basic module formatting (#4784) * Add Format for Stmt * Implement basic module formatting This implements formatting each statement in a module with a hard line break in between, so that we can start formatting statements. Basic testing is done by the snapshots --- .gitignore | 1 + crates/ruff_python_formatter/src/lib.rs | 5 +- .../src/module/mod_module.rs | 10 +- ...ttribute_access_on_number_literals_py.snap | 11 +- ...lack_test__class_blank_parentheses_py.snap | 14 +- ...er__tests__black_test__collections_py.snap | 55 ++- ...est__comment_after_escaped_newline_py.snap | 8 +- ...tter__tests__black_test__comments2_py.snap | 67 ++- ...tter__tests__black_test__comments3_py.snap | 180 +++++++ ...tter__tests__black_test__comments4_py.snap | 338 ++++++++++++++ ...tter__tests__black_test__comments5_py.snap | 288 ++++++++++++ ...tter__tests__black_test__comments6_py.snap | 442 ++++++++++++++++++ ...tter__tests__black_test__comments9_py.snap | 182 ++++---- ..._test__comments_non_breaking_space_py.snap | 31 +- ...atter__tests__black_test__comments_py.snap | 111 +++-- ...ing_no_extra_empty_line_before_eof_py.snap | 43 ++ ...sts__black_test__docstring_preview_py.snap | 64 ++- ...tter__tests__black_test__docstring_py.snap | 252 ++++------ ...er__tests__black_test__empty_lines_py.snap | 33 +- ...ter__tests__black_test__expression_py.snap | 16 +- ...tter__tests__black_test__fmtonoff2_py.snap | 174 +++++++ ...tter__tests__black_test__fmtonoff3_py.snap | 23 +- ...tter__tests__black_test__fmtonoff4_py.snap | 11 +- ...tter__tests__black_test__fmtonoff5_py.snap | 89 ++-- ...atter__tests__black_test__fmtonoff_py.snap | 122 +++-- ...atter__tests__black_test__fmtskip2_py.snap | 7 +- ...atter__tests__black_test__fmtskip3_py.snap | 13 +- ...atter__tests__black_test__fmtskip4_py.snap | 7 +- ...atter__tests__black_test__fmtskip6_py.snap | 49 ++ ...atter__tests__black_test__fmtskip7_py.snap | 12 +- ...atter__tests__black_test__fmtskip8_py.snap | 253 ++++++++++ ...atter__tests__black_test__fmtskip_py.snap} | 39 +- ...tter__tests__black_test__function2_py.snap | 13 +- ...atter__tests__black_test__function_py.snap | 18 +- ...lack_test__function_trailing_comma_py.snap | 78 ++-- ..._tests__black_test__import_spacing_py.snap | 48 +- ..._black_test__one_element_subscript_py.snap | 23 +- ...ests__black_test__power_op_spacing_py.snap | 53 ++- ...test__prefer_rhs_split_reformatted_py.snap | 22 +- ...s__black_test__remove_await_parens_py.snap | 135 +++--- ...__black_test__remove_except_parens_py.snap | 39 +- ...s__black_test__remove_for_brackets_py.snap | 29 +- ...move_newline_after_code_block_open_py.snap | 139 ++---- ...__tests__black_test__remove_parens_py.snap | 65 +-- ...k_test__return_annotation_brackets_py.snap | 136 +++--- ...ck_test__skip_magic_trailing_comma_py.snap | 48 +- ...rmatter__tests__black_test__slices_py.snap | 38 +- ...tests__black_test__string_prefixes_py.snap | 25 +- ...matter__tests__black_test__torture_py.snap | 40 +- ...t__trailing_comma_optional_parens1_py.snap | 14 +- ...__trailing_commas_in_leading_parts_py.snap | 51 +- ...black_test__tricky_unicode_symbols_py.snap | 61 +++ ...er__tests__black_test__tupleassign_py.snap | 16 +- .../src/statement/mod.rs | 56 +++ 54 files changed, 2926 insertions(+), 1171 deletions(-) create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments3_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments4_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments5_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments6_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_no_extra_empty_line_before_eof_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff2_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip6_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip8_py.snap rename crates/ruff_python_formatter/src/snapshots/{ruff_python_formatter__tests__black_test__beginning_backslash_py.snap => ruff_python_formatter__tests__black_test__fmtskip_py.snap} (56%) create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__tricky_unicode_symbols_py.snap diff --git a/.gitignore b/.gitignore index de3c456e67..55de3ba557 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ ruff-old github_search*.jsonl schemastore .venv* +scratch.py ### # Rust.gitignore diff --git a/crates/ruff_python_formatter/src/lib.rs b/crates/ruff_python_formatter/src/lib.rs index f24e8eb7f5..3c8a581c73 100644 --- a/crates/ruff_python_formatter/src/lib.rs +++ b/crates/ruff_python_formatter/src/lib.rs @@ -180,11 +180,8 @@ if True: print( "hi" ) # trailing "#; - let expected = r#" -# preceding -if True: + let expected = r#"if True: print( "hi" ) -# trailing "#; let actual = format_module(input)?.as_code().to_string(); assert_eq!(expected, actual); diff --git a/crates/ruff_python_formatter/src/module/mod_module.rs b/crates/ruff_python_formatter/src/module/mod_module.rs index ee353e6e5c..a2d0370b5c 100644 --- a/crates/ruff_python_formatter/src/module/mod_module.rs +++ b/crates/ruff_python_formatter/src/module/mod_module.rs @@ -1,5 +1,8 @@ -use crate::{verbatim_text, FormatNodeRule, PyFormatter}; +use crate::AsFormat; +use crate::{FormatNodeRule, PyFormatter}; +use ruff_formatter::prelude::hard_line_break; use ruff_formatter::{write, Buffer, FormatResult}; + use rustpython_parser::ast::ModModule; #[derive(Default)] @@ -7,6 +10,9 @@ pub struct FormatModModule; impl FormatNodeRule for FormatModModule { fn fmt_fields(&self, item: &ModModule, f: &mut PyFormatter) -> FormatResult<()> { - write!(f, [verbatim_text(item.range)]) + for stmt in &item.body { + write!(f, [stmt.format(), hard_line_break()])?; + } + Ok(()) } } diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__attribute_access_on_number_literals_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__attribute_access_on_number_literals_py.snap index 0128d6ed45..2849790d1f 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__attribute_access_on_number_literals_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__attribute_access_on_number_literals_py.snap @@ -35,7 +35,7 @@ y = 100(no) ```diff --- Black +++ Ruff -@@ -1,21 +1,21 @@ +@@ -1,22 +1,20 @@ -x = (123456789).bit_count() +x = 123456789 .bit_count() x = (123456).__abs__() @@ -53,6 +53,8 @@ y = 100(no) -x = 0o777.real -x = (0.000000006).hex() -x = -100.0000j +- +-if (10).real: +x = .1.is_integer() +x = 1. .imag +x = 1E+1.imag @@ -67,12 +69,11 @@ y = 100(no) +x = 0O777 .real +x = 0.000000006 .hex() +x = -100.0000J - --if (10).real: +if 10 .real: ... - +- y = 100[no] + y = 100(no) ``` ## Ruff Output @@ -94,10 +95,8 @@ x = 0B1011 .conjugate() x = 0O777 .real x = 0.000000006 .hex() x = -100.0000J - if 10 .real: ... - y = 100[no] y = 100(no) ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_blank_parentheses_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_blank_parentheses_py.snap index 12cec64049..5e1f3eff02 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_blank_parentheses_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_blank_parentheses_py.snap @@ -36,7 +36,7 @@ class NormalClass ( ```diff --- Black +++ Ruff -@@ -1,30 +1,23 @@ +@@ -1,30 +1,21 @@ -class SimpleClassWithBlankParentheses: +class SimpleClassWithBlankParentheses(): pass @@ -50,13 +50,13 @@ class NormalClass ( def test_func(self): return None - -+class ClassWithEmptyFunc(object): - --class ClassWithEmptyFunc(object): +- + class ClassWithEmptyFunc(object): ++ def func_with_blank_parentheses(): return 5 - - +- +- def public_func_with_blank_parentheses(): return None - @@ -89,8 +89,6 @@ class ClassWithEmptyFunc(object): def func_with_blank_parentheses(): return 5 - - def public_func_with_blank_parentheses(): return None def class_under_the_func_with_blank_parentheses(): diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__collections_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__collections_py.snap index 47db08c75f..8858eb3170 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__collections_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__collections_py.snap @@ -84,10 +84,27 @@ if True: ```diff --- Black +++ Ruff -@@ -18,44 +18,26 @@ +@@ -1,99 +1,57 @@ + import core, time, a +- + from . import A, B, C +- +-# keeps existing trailing comma + from foo import ( + bar, + ) +- +-# also keeps existing structure + from foo import ( + baz, + qux, + ) +- +-# `as` works as well + from foo import ( xyzzy as magic, ) - +- -a = { - 1, - 2, @@ -137,30 +154,32 @@ if True: - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wraps %s" - % bar -) +- +-# looping over a 1-tuple should also not get wrapped +y = {"oneple": (1,),} +assert False, ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wraps %s" % bar) - - # looping over a 1-tuple should also not get wrapped for x in (1,): -@@ -63,13 +45,9 @@ + pass for (x,) in (1,), (2,), (3,): pass - +- -[ - 1, - 2, - 3, -] -+[1, 2, 3,] - +- -division_result_tuple = (6 / 2,) ++[1, 2, 3,] +division_result_tuple = (6/2,) print("foo %r", (foo.bar,)) - +- if True: -@@ -79,21 +57,15 @@ + IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING = ( + Config.IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING + | {pylons.controllers.WSGIController} ) - +- if True: - ec2client.get_waiter("instance_stopped").wait( + ec2client.get_waiter('instance_stopped').wait( @@ -191,25 +210,17 @@ if True: ```py import core, time, a - from . import A, B, C - -# keeps existing trailing comma from foo import ( bar, ) - -# also keeps existing structure from foo import ( baz, qux, ) - -# `as` works as well from foo import ( xyzzy as magic, ) - a = {1,2,3,} b = { 1,2, @@ -230,24 +241,18 @@ nested_long_lines = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbb x = {"oneple": (1,)} y = {"oneple": (1,),} assert False, ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wraps %s" % bar) - -# looping over a 1-tuple should also not get wrapped for x in (1,): pass for (x,) in (1,), (2,), (3,): pass - [1, 2, 3,] - division_result_tuple = (6/2,) print("foo %r", (foo.bar,)) - if True: IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING = ( Config.IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING | {pylons.controllers.WSGIController} ) - if True: ec2client.get_waiter('instance_stopped').wait( InstanceIds=[instance.id], diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comment_after_escaped_newline_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comment_after_escaped_newline_py.snap index 5633eef26a..86d56db433 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comment_after_escaped_newline_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comment_after_escaped_newline_py.snap @@ -22,13 +22,13 @@ def bobtwo(): \ ```diff --- Black +++ Ruff -@@ -1,6 +1,9 @@ +@@ -1,6 +1,7 @@ -def bob(): # pylint: disable=W9016 +def bob(): \ + # pylint: disable=W9016 pass - - +- +- -def bobtwo(): # some comment here +def bobtwo(): \ + \ @@ -42,8 +42,6 @@ def bobtwo(): \ def bob(): \ # pylint: disable=W9016 pass - - def bobtwo(): \ \ # some comment here diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments2_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments2_py.snap index f46a4ca2a1..b4e5b4d377 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments2_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments2_py.snap @@ -178,7 +178,7 @@ instruction()#comment with bad spacing ```diff --- Black +++ Ruff -@@ -1,39 +1,40 @@ +@@ -1,39 +1,36 @@ from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( - MyLovelyCompanyTeamProjectComponent, # NOT DRY + MyLovelyCompanyTeamProjectComponent # NOT DRY @@ -187,9 +187,9 @@ instruction()#comment with bad spacing - MyLovelyCompanyTeamProjectComponent as component, # DRY + MyLovelyCompanyTeamProjectComponent as component # DRY ) - - # Please keep __all__ alphabetized within each category. - +- +-# Please keep __all__ alphabetized within each category. +- __all__ = [ # Super-special typing primitives. - "Any", @@ -227,7 +227,7 @@ instruction()#comment with bad spacing + 'NamedTuple', # Not really a type. + 'Generator', ] - +- not_shareables = [ # singletons True, @@ -238,10 +238,11 @@ instruction()#comment with bad spacing # builtin types and objects type, object, -@@ -48,20 +49,23 @@ +@@ -47,21 +44,20 @@ + Cheese("Wensleydale"), SubBytes(b"spam"), ] - +- -if "PYTHON" in os.environ: +if 'PYTHON' in os.environ: add_compiler(compiler_from_env()) @@ -250,10 +251,10 @@ instruction()#comment with bad spacing - # add_compiler(compiler) + # add_compiler(compiler) add_compiler(compilers[(7.0, 32)]) - # add_compiler(compilers[(7.1, 64)]) +- # add_compiler(compilers[(7.1, 64)]) - - - # Comment before function. +- +-# Comment before function. def inline_comments_in_brackets_ruin_everything(): if typedargslist: - parameters.children = [children[0], body, children[-1]] # (1 # )1 @@ -266,7 +267,7 @@ instruction()#comment with bad spacing children[0], body, children[-1], # type: ignore -@@ -73,49 +77,42 @@ +@@ -73,49 +69,42 @@ parameters.children[-1], # )2 ] parameters.children = [parameters.what_if_this_was_actually_long.children[0], body, parameters.children[-1]] # type: ignore @@ -339,7 +340,7 @@ instruction()#comment with bad spacing ] lcomp2 = [ # hello -@@ -127,7 +124,7 @@ +@@ -127,7 +116,7 @@ ] lcomp3 = [ # This one is actually too long to fit in a single line. @@ -348,7 +349,7 @@ instruction()#comment with bad spacing # yup for element in collection.select_elements() # right -@@ -140,34 +137,26 @@ +@@ -140,34 +129,18 @@ # and round and round we go # and round and round we go @@ -362,7 +363,7 @@ instruction()#comment with bad spacing + Leaf(token.NEWLINE, '\n') # FIXME: \r\n? + ], ) - +- - -CONFIG_FILES = ( - [ @@ -371,27 +372,27 @@ instruction()#comment with bad spacing - + SHARED_CONFIG_FILES - + USER_CONFIG_FILES -) # type: Final -+CONFIG_FILES = [CONFIG_FILE, ] + SHARED_CONFIG_FILES + USER_CONFIG_FILES # type: Final - - +- ++CONFIG_FILES = [CONFIG_FILE, ] + SHARED_CONFIG_FILES + USER_CONFIG_FILES class Test: def _init_host(self, parsed) -> None: - if parsed.hostname is None or not parsed.hostname.strip(): # type: ignore + if (parsed.hostname is None or # type: ignore + not parsed.hostname.strip()): pass - - - ####################### - ### SECTION COMMENT ### - ####################### - - +- +-####################### +-### SECTION COMMENT ### +-####################### +- +- -instruction() # comment with bad spacing - -# END COMMENTS -# MORE END COMMENTS -+instruction()#comment with bad spacing ++instruction() ``` ## Ruff Output @@ -403,9 +404,6 @@ from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( MyLovelyCompanyTeamProjectComponent as component # DRY ) - -# Please keep __all__ alphabetized within each category. - __all__ = [ # Super-special typing primitives. 'Any', @@ -428,7 +426,6 @@ __all__ = [ 'NamedTuple', # Not really a type. 'Generator', ] - not_shareables = [ # singletons True, @@ -447,16 +444,12 @@ not_shareables = [ Cheese("Wensleydale"), SubBytes(b"spam"), ] - if 'PYTHON' in os.environ: add_compiler(compiler_from_env()) else: # for compiler in compilers.values(): # add_compiler(compiler) add_compiler(compilers[(7.0, 32)]) - # add_compiler(compilers[(7.1, 64)]) - -# Comment before function. def inline_comments_in_brackets_ruin_everything(): if typedargslist: parameters.children = [ @@ -544,21 +537,13 @@ short Leaf(token.NEWLINE, '\n') # FIXME: \r\n? ], ) - -CONFIG_FILES = [CONFIG_FILE, ] + SHARED_CONFIG_FILES + USER_CONFIG_FILES # type: Final - +CONFIG_FILES = [CONFIG_FILE, ] + SHARED_CONFIG_FILES + USER_CONFIG_FILES class Test: def _init_host(self, parsed) -> None: if (parsed.hostname is None or # type: ignore not parsed.hostname.strip()): pass - -####################### -### SECTION COMMENT ### -####################### - - -instruction()#comment with bad spacing +instruction() ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments3_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments3_py.snap new file mode 100644 index 0000000000..9499d9093b --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments3_py.snap @@ -0,0 +1,180 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments3.py +--- +## Input + +```py +# The percent-percent comments are Spyder IDE cells. + + +# %% +def func(): + x = """ + a really long string + """ + lcomp3 = [ + # This one is actually too long to fit in a single line. + element.split("\n", 1)[0] + # yup + for element in collection.select_elements() + # right + if element is not None + ] + # Capture each of the exceptions in the MultiError along with each of their causes and contexts + if isinstance(exc_value, MultiError): + embedded = [] + for exc in exc_value.exceptions: + if exc not in _seen: + embedded.append( + # This should be left alone (before) + traceback.TracebackException.from_exception( + exc, + limit=limit, + lookup_lines=lookup_lines, + capture_locals=capture_locals, + # copy the set of _seen exceptions so that duplicates + # shared between sub-exceptions are not omitted + _seen=set(_seen), + ) + # This should be left alone (after) + ) + + # everything is fine if the expression isn't nested + traceback.TracebackException.from_exception( + exc, + limit=limit, + lookup_lines=lookup_lines, + capture_locals=capture_locals, + # copy the set of _seen exceptions so that duplicates + # shared between sub-exceptions are not omitted + _seen=set(_seen), + ) + + +# %% +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,7 +1,3 @@ +-# The percent-percent comments are Spyder IDE cells. +- +- +-# %% + def func(): + x = """ + a really long string +@@ -43,6 +39,3 @@ + # shared between sub-exceptions are not omitted + _seen=set(_seen), + ) +- +- +-# %% +``` + +## Ruff Output + +```py +def func(): + x = """ + a really long string + """ + lcomp3 = [ + # This one is actually too long to fit in a single line. + element.split("\n", 1)[0] + # yup + for element in collection.select_elements() + # right + if element is not None + ] + # Capture each of the exceptions in the MultiError along with each of their causes and contexts + if isinstance(exc_value, MultiError): + embedded = [] + for exc in exc_value.exceptions: + if exc not in _seen: + embedded.append( + # This should be left alone (before) + traceback.TracebackException.from_exception( + exc, + limit=limit, + lookup_lines=lookup_lines, + capture_locals=capture_locals, + # copy the set of _seen exceptions so that duplicates + # shared between sub-exceptions are not omitted + _seen=set(_seen), + ) + # This should be left alone (after) + ) + + # everything is fine if the expression isn't nested + traceback.TracebackException.from_exception( + exc, + limit=limit, + lookup_lines=lookup_lines, + capture_locals=capture_locals, + # copy the set of _seen exceptions so that duplicates + # shared between sub-exceptions are not omitted + _seen=set(_seen), + ) +``` + +## Black Output + +```py +# The percent-percent comments are Spyder IDE cells. + + +# %% +def func(): + x = """ + a really long string + """ + lcomp3 = [ + # This one is actually too long to fit in a single line. + element.split("\n", 1)[0] + # yup + for element in collection.select_elements() + # right + if element is not None + ] + # Capture each of the exceptions in the MultiError along with each of their causes and contexts + if isinstance(exc_value, MultiError): + embedded = [] + for exc in exc_value.exceptions: + if exc not in _seen: + embedded.append( + # This should be left alone (before) + traceback.TracebackException.from_exception( + exc, + limit=limit, + lookup_lines=lookup_lines, + capture_locals=capture_locals, + # copy the set of _seen exceptions so that duplicates + # shared between sub-exceptions are not omitted + _seen=set(_seen), + ) + # This should be left alone (after) + ) + + # everything is fine if the expression isn't nested + traceback.TracebackException.from_exception( + exc, + limit=limit, + lookup_lines=lookup_lines, + capture_locals=capture_locals, + # copy the set of _seen exceptions so that duplicates + # shared between sub-exceptions are not omitted + _seen=set(_seen), + ) + + +# %% +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments4_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments4_py.snap new file mode 100644 index 0000000000..77be147ea1 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments4_py.snap @@ -0,0 +1,338 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments4.py +--- +## Input + +```py +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent, # NOT DRY +) +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent as component, # DRY +) + + +class C: + @pytest.mark.parametrize( + ("post_data", "message"), + [ + # metadata_version errors. + ( + {}, + "None is an invalid value for Metadata-Version. Error: This field is" + " required. see" + " https://packaging.python.org/specifications/core-metadata", + ), + ( + {"metadata_version": "-1"}, + "'-1' is an invalid value for Metadata-Version. Error: Unknown Metadata" + " Version see" + " https://packaging.python.org/specifications/core-metadata", + ), + # name errors. + ( + {"metadata_version": "1.2"}, + "'' is an invalid value for Name. Error: This field is required. see" + " https://packaging.python.org/specifications/core-metadata", + ), + ( + {"metadata_version": "1.2", "name": "foo-"}, + "'foo-' is an invalid value for Name. Error: Must start and end with a" + " letter or numeral and contain only ascii numeric and '.', '_' and" + " '-'. see https://packaging.python.org/specifications/core-metadata", + ), + # version errors. + ( + {"metadata_version": "1.2", "name": "example"}, + "'' is an invalid value for Version. Error: This field is required. see" + " https://packaging.python.org/specifications/core-metadata", + ), + ( + {"metadata_version": "1.2", "name": "example", "version": "dog"}, + "'dog' is an invalid value for Version. Error: Must start and end with" + " a letter or numeral and contain only ascii numeric and '.', '_' and" + " '-'. see https://packaging.python.org/specifications/core-metadata", + ), + ], + ) + def test_fails_invalid_post_data( + self, pyramid_config, db_request, post_data, message + ): + pyramid_config.testing_securitypolicy(userid=1) + db_request.POST = MultiDict(post_data) + + +def foo(list_a, list_b): + results = ( + User.query.filter(User.foo == "bar") + .filter( # Because foo. + db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) + ) + .filter(User.xyz.is_(None)) + # Another comment about the filtering on is_quux goes here. + .filter(db.not_(User.is_pending.astext.cast(db.Boolean).is_(True))) + .order_by(User.created_at.desc()) + .with_for_update(key_share=True) + .all() + ) + return results + + +def foo2(list_a, list_b): + # Standalone comment reasonably placed. + return ( + User.query.filter(User.foo == "bar") + .filter( + db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) + ) + .filter(User.xyz.is_(None)) + ) + + +def foo3(list_a, list_b): + return ( + # Standalone comment but weirdly placed. + User.query.filter(User.foo == "bar") + .filter( + db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) + ) + .filter(User.xyz.is_(None)) + ) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -4,8 +4,6 @@ + from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent as component, # DRY + ) +- +- + class C: + @pytest.mark.parametrize( + ("post_data", "message"), +@@ -54,8 +52,6 @@ + ): + pyramid_config.testing_securitypolicy(userid=1) + db_request.POST = MultiDict(post_data) +- +- + def foo(list_a, list_b): + results = ( + User.query.filter(User.foo == "bar") +@@ -70,8 +66,6 @@ + .all() + ) + return results +- +- + def foo2(list_a, list_b): + # Standalone comment reasonably placed. + return ( +@@ -81,8 +75,6 @@ + ) + .filter(User.xyz.is_(None)) + ) +- +- + def foo3(list_a, list_b): + return ( + # Standalone comment but weirdly placed. +``` + +## Ruff Output + +```py +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent, # NOT DRY +) +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent as component, # DRY +) +class C: + @pytest.mark.parametrize( + ("post_data", "message"), + [ + # metadata_version errors. + ( + {}, + "None is an invalid value for Metadata-Version. Error: This field is" + " required. see" + " https://packaging.python.org/specifications/core-metadata", + ), + ( + {"metadata_version": "-1"}, + "'-1' is an invalid value for Metadata-Version. Error: Unknown Metadata" + " Version see" + " https://packaging.python.org/specifications/core-metadata", + ), + # name errors. + ( + {"metadata_version": "1.2"}, + "'' is an invalid value for Name. Error: This field is required. see" + " https://packaging.python.org/specifications/core-metadata", + ), + ( + {"metadata_version": "1.2", "name": "foo-"}, + "'foo-' is an invalid value for Name. Error: Must start and end with a" + " letter or numeral and contain only ascii numeric and '.', '_' and" + " '-'. see https://packaging.python.org/specifications/core-metadata", + ), + # version errors. + ( + {"metadata_version": "1.2", "name": "example"}, + "'' is an invalid value for Version. Error: This field is required. see" + " https://packaging.python.org/specifications/core-metadata", + ), + ( + {"metadata_version": "1.2", "name": "example", "version": "dog"}, + "'dog' is an invalid value for Version. Error: Must start and end with" + " a letter or numeral and contain only ascii numeric and '.', '_' and" + " '-'. see https://packaging.python.org/specifications/core-metadata", + ), + ], + ) + def test_fails_invalid_post_data( + self, pyramid_config, db_request, post_data, message + ): + pyramid_config.testing_securitypolicy(userid=1) + db_request.POST = MultiDict(post_data) +def foo(list_a, list_b): + results = ( + User.query.filter(User.foo == "bar") + .filter( # Because foo. + db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) + ) + .filter(User.xyz.is_(None)) + # Another comment about the filtering on is_quux goes here. + .filter(db.not_(User.is_pending.astext.cast(db.Boolean).is_(True))) + .order_by(User.created_at.desc()) + .with_for_update(key_share=True) + .all() + ) + return results +def foo2(list_a, list_b): + # Standalone comment reasonably placed. + return ( + User.query.filter(User.foo == "bar") + .filter( + db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) + ) + .filter(User.xyz.is_(None)) + ) +def foo3(list_a, list_b): + return ( + # Standalone comment but weirdly placed. + User.query.filter(User.foo == "bar") + .filter( + db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) + ) + .filter(User.xyz.is_(None)) + ) +``` + +## Black Output + +```py +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent, # NOT DRY +) +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent as component, # DRY +) + + +class C: + @pytest.mark.parametrize( + ("post_data", "message"), + [ + # metadata_version errors. + ( + {}, + "None is an invalid value for Metadata-Version. Error: This field is" + " required. see" + " https://packaging.python.org/specifications/core-metadata", + ), + ( + {"metadata_version": "-1"}, + "'-1' is an invalid value for Metadata-Version. Error: Unknown Metadata" + " Version see" + " https://packaging.python.org/specifications/core-metadata", + ), + # name errors. + ( + {"metadata_version": "1.2"}, + "'' is an invalid value for Name. Error: This field is required. see" + " https://packaging.python.org/specifications/core-metadata", + ), + ( + {"metadata_version": "1.2", "name": "foo-"}, + "'foo-' is an invalid value for Name. Error: Must start and end with a" + " letter or numeral and contain only ascii numeric and '.', '_' and" + " '-'. see https://packaging.python.org/specifications/core-metadata", + ), + # version errors. + ( + {"metadata_version": "1.2", "name": "example"}, + "'' is an invalid value for Version. Error: This field is required. see" + " https://packaging.python.org/specifications/core-metadata", + ), + ( + {"metadata_version": "1.2", "name": "example", "version": "dog"}, + "'dog' is an invalid value for Version. Error: Must start and end with" + " a letter or numeral and contain only ascii numeric and '.', '_' and" + " '-'. see https://packaging.python.org/specifications/core-metadata", + ), + ], + ) + def test_fails_invalid_post_data( + self, pyramid_config, db_request, post_data, message + ): + pyramid_config.testing_securitypolicy(userid=1) + db_request.POST = MultiDict(post_data) + + +def foo(list_a, list_b): + results = ( + User.query.filter(User.foo == "bar") + .filter( # Because foo. + db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) + ) + .filter(User.xyz.is_(None)) + # Another comment about the filtering on is_quux goes here. + .filter(db.not_(User.is_pending.astext.cast(db.Boolean).is_(True))) + .order_by(User.created_at.desc()) + .with_for_update(key_share=True) + .all() + ) + return results + + +def foo2(list_a, list_b): + # Standalone comment reasonably placed. + return ( + User.query.filter(User.foo == "bar") + .filter( + db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) + ) + .filter(User.xyz.is_(None)) + ) + + +def foo3(list_a, list_b): + return ( + # Standalone comment but weirdly placed. + User.query.filter(User.foo == "bar") + .filter( + db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) + ) + .filter(User.xyz.is_(None)) + ) +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments5_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments5_py.snap new file mode 100644 index 0000000000..8c41235ead --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments5_py.snap @@ -0,0 +1,288 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments5.py +--- +## Input + +```py +while True: + if something.changed: + do.stuff() # trailing comment + # Comment belongs to the `if` block. + # This one belongs to the `while` block. + + # Should this one, too? I guess so. + +# This one is properly standalone now. + +for i in range(100): + # first we do this + if i % 33 == 0: + break + + # then we do this + print(i) + # and finally we loop around + +with open(some_temp_file) as f: + data = f.read() + +try: + with open(some_other_file) as w: + w.write(data) + +except OSError: + print("problems") + +import sys + + +# leading function comment +def wat(): + ... + # trailing function comment + + +# SECTION COMMENT + + +# leading 1 +@deco1 +# leading 2 +@deco2(with_args=True) +# leading 3 +@deco3 +def decorated1(): + ... + + +# leading 1 +@deco1 +# leading 2 +@deco2(with_args=True) +# leading function comment +def decorated1(): + ... + + +# Note: this is fixed in +# 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(): + ... + + +if __name__ == "__main__": + main() +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,13 +1,6 @@ + while True: + if something.changed: +- do.stuff() # trailing comment +- # Comment belongs to the `if` block. +- # This one belongs to the `while` block. +- +- # Should this one, too? I guess so. +- +-# This one is properly standalone now. +- ++ do.stuff() + for i in range(100): + # first we do this + if i % 33 == 0: +@@ -15,31 +8,17 @@ + + # then we do this + print(i) +- # and finally we loop around +- + with open(some_temp_file) as f: + data = f.read() +- + try: + with open(some_other_file) as w: + w.write(data) + + except OSError: + print("problems") +- + import sys +- +- +-# leading function comment + def wat(): + ... +- # trailing function comment +- +- +-# SECTION COMMENT +- +- +-# leading 1 + @deco1 + # leading 2 + @deco2(with_args=True) +@@ -47,27 +26,14 @@ + @deco3 + def decorated1(): + ... +- +- +-# leading 1 + @deco1 + # leading 2 + @deco2(with_args=True) + # leading function comment + def decorated1(): + ... +- +- +-# Note: this is fixed in +-# 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(): + ... +- +- + if __name__ == "__main__": + main() +``` + +## Ruff Output + +```py +while True: + if something.changed: + do.stuff() +for i in range(100): + # first we do this + if i % 33 == 0: + break + + # then we do this + print(i) +with open(some_temp_file) as f: + data = f.read() +try: + with open(some_other_file) as w: + w.write(data) + +except OSError: + print("problems") +import sys +def wat(): + ... +@deco1 +# leading 2 +@deco2(with_args=True) +# leading 3 +@deco3 +def decorated1(): + ... +@deco1 +# leading 2 +@deco2(with_args=True) +# leading function comment +def decorated1(): + ... +some_instruction +def g(): + ... +if __name__ == "__main__": + main() +``` + +## Black Output + +```py +while True: + if something.changed: + do.stuff() # trailing comment + # Comment belongs to the `if` block. + # This one belongs to the `while` block. + + # Should this one, too? I guess so. + +# This one is properly standalone now. + +for i in range(100): + # first we do this + if i % 33 == 0: + break + + # then we do this + print(i) + # and finally we loop around + +with open(some_temp_file) as f: + data = f.read() + +try: + with open(some_other_file) as w: + w.write(data) + +except OSError: + print("problems") + +import sys + + +# leading function comment +def wat(): + ... + # trailing function comment + + +# SECTION COMMENT + + +# leading 1 +@deco1 +# leading 2 +@deco2(with_args=True) +# leading 3 +@deco3 +def decorated1(): + ... + + +# leading 1 +@deco1 +# leading 2 +@deco2(with_args=True) +# leading function comment +def decorated1(): + ... + + +# Note: this is fixed in +# 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(): + ... + + +if __name__ == "__main__": + main() +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments6_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments6_py.snap new file mode 100644 index 0000000000..7fcf9d102f --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments6_py.snap @@ -0,0 +1,442 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments6.py +--- +## Input + +```py +from typing import Any, Tuple + + +def f( + a, # type: int +): + pass + + +# test type comments +def f(a, b, c, d, e, f, g, h, i): + # type: (int, int, int, int, int, int, int, int, int) -> None + pass + + +def f( + a, # type: int + b, # type: int + c, # type: int + d, # type: int + e, # type: int + f, # type: int + g, # type: int + h, # type: int + i, # type: int +): + # type: (...) -> None + pass + + +def f( + arg, # type: int + *args, # type: *Any + default=False, # type: bool + **kwargs, # type: **Any +): + # type: (...) -> None + pass + + +def f( + a, # type: int + b, # type: int + c, # type: int + d, # type: int +): + # type: (...) -> None + + element = 0 # type: int + another_element = 1 # type: float + another_element_with_long_name = 2 # type: int + another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style = ( + 3 + ) # type: int + an_element_with_a_long_value = calls() or more_calls() and more() # type: bool + + tup = ( + another_element, + another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style, + ) # type: Tuple[int, int] + + a = ( + element + + another_element + + another_element_with_long_name + + element + + another_element + + another_element_with_long_name + ) # type: int + + +def f( + x, # not a type comment + y, # type: int +): + # type: (...) -> None + pass + + +def f( + x, # not a type comment +): # type: (int) -> None + pass + + +def func( + a=some_list[0], # type: int +): # type: () -> int + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + a[-1], # type: ignore + ) + + c = call( + "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa" # type: ignore + ) + + +result = ( # aaa + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +) + +AAAAAAAAAAAAA = [AAAAAAAAAAAAA] + SHARED_AAAAAAAAAAAAA + USER_AAAAAAAAAAAAA + AAAAAAAAAAAAA # type: ignore + +call_to_some_function_asdf( + foo, + [AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, BBBBBBBBBBBB], # type: ignore +) + +aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) # type: ignore[arg-type] +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,18 +1,11 @@ + from typing import Any, Tuple +- +- + def f( + a, # type: int + ): + pass +- +- +-# test type comments + def f(a, b, c, d, e, f, g, h, i): + # type: (int, int, int, int, int, int, int, int, int) -> None + pass +- +- + def f( + a, # type: int + b, # type: int +@@ -26,8 +19,6 @@ + ): + # type: (...) -> None + pass +- +- + def f( + arg, # type: int + *args, # type: *Any +@@ -36,8 +27,6 @@ + ): + # type: (...) -> None + pass +- +- + def f( + a, # type: int + b, # type: int +@@ -66,23 +55,17 @@ + + element + + another_element + + another_element_with_long_name +- ) # type: int +- +- ++ ) + def f( + x, # not a type comment + y, # type: int + ): + # type: (...) -> None + pass +- +- + def f( + x, # not a type comment + ): # type: (int) -> None + pass +- +- + def func( + a=some_list[0], # type: int + ): # type: () -> int +@@ -102,17 +85,12 @@ + c = call( + "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa" # type: ignore + ) +- +- + result = ( # aaa + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + ) +- +-AAAAAAAAAAAAA = [AAAAAAAAAAAAA] + SHARED_AAAAAAAAAAAAA + USER_AAAAAAAAAAAAA + AAAAAAAAAAAAA # type: ignore +- ++AAAAAAAAAAAAA = [AAAAAAAAAAAAA] + SHARED_AAAAAAAAAAAAA + USER_AAAAAAAAAAAAA + AAAAAAAAAAAAA + call_to_some_function_asdf( + foo, + [AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, BBBBBBBBBBBB], # type: ignore + ) +- +-aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) # type: ignore[arg-type] ++aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) +``` + +## Ruff Output + +```py +from typing import Any, Tuple +def f( + a, # type: int +): + pass +def f(a, b, c, d, e, f, g, h, i): + # type: (int, int, int, int, int, int, int, int, int) -> None + pass +def f( + a, # type: int + b, # type: int + c, # type: int + d, # type: int + e, # type: int + f, # type: int + g, # type: int + h, # type: int + i, # type: int +): + # type: (...) -> None + pass +def f( + arg, # type: int + *args, # type: *Any + default=False, # type: bool + **kwargs, # type: **Any +): + # type: (...) -> None + pass +def f( + a, # type: int + b, # type: int + c, # type: int + d, # type: int +): + # type: (...) -> None + + element = 0 # type: int + another_element = 1 # type: float + another_element_with_long_name = 2 # type: int + another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style = ( + 3 + ) # type: int + an_element_with_a_long_value = calls() or more_calls() and more() # type: bool + + tup = ( + another_element, + another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style, + ) # type: Tuple[int, int] + + a = ( + element + + another_element + + another_element_with_long_name + + element + + another_element + + another_element_with_long_name + ) +def f( + x, # not a type comment + y, # type: int +): + # type: (...) -> None + pass +def f( + x, # not a type comment +): # type: (int) -> None + pass +def func( + a=some_list[0], # type: int +): # type: () -> int + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + a[-1], # type: ignore + ) + + c = call( + "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa" # type: ignore + ) +result = ( # aaa + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +) +AAAAAAAAAAAAA = [AAAAAAAAAAAAA] + SHARED_AAAAAAAAAAAAA + USER_AAAAAAAAAAAAA + AAAAAAAAAAAAA +call_to_some_function_asdf( + foo, + [AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, BBBBBBBBBBBB], # type: ignore +) +aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) +``` + +## Black Output + +```py +from typing import Any, Tuple + + +def f( + a, # type: int +): + pass + + +# test type comments +def f(a, b, c, d, e, f, g, h, i): + # type: (int, int, int, int, int, int, int, int, int) -> None + pass + + +def f( + a, # type: int + b, # type: int + c, # type: int + d, # type: int + e, # type: int + f, # type: int + g, # type: int + h, # type: int + i, # type: int +): + # type: (...) -> None + pass + + +def f( + arg, # type: int + *args, # type: *Any + default=False, # type: bool + **kwargs, # type: **Any +): + # type: (...) -> None + pass + + +def f( + a, # type: int + b, # type: int + c, # type: int + d, # type: int +): + # type: (...) -> None + + element = 0 # type: int + another_element = 1 # type: float + another_element_with_long_name = 2 # type: int + another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style = ( + 3 + ) # type: int + an_element_with_a_long_value = calls() or more_calls() and more() # type: bool + + tup = ( + another_element, + another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style, + ) # type: Tuple[int, int] + + a = ( + element + + another_element + + another_element_with_long_name + + element + + another_element + + another_element_with_long_name + ) # type: int + + +def f( + x, # not a type comment + y, # type: int +): + # type: (...) -> None + pass + + +def f( + x, # not a type comment +): # type: (int) -> None + pass + + +def func( + a=some_list[0], # type: int +): # type: () -> int + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + a[-1], # type: ignore + ) + + c = call( + "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa" # type: ignore + ) + + +result = ( # aaa + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +) + +AAAAAAAAAAAAA = [AAAAAAAAAAAAA] + SHARED_AAAAAAAAAAAAA + USER_AAAAAAAAAAAAA + AAAAAAAAAAAAA # type: ignore + +call_to_some_function_asdf( + foo, + [AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, BBBBBBBBBBBB], # type: ignore +) + +aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) # type: ignore[arg-type] +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments9_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments9_py.snap index 2e109bb331..7ca7b4f04b 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments9_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments9_py.snap @@ -152,84 +152,102 @@ def bar(): ```diff --- Black +++ Ruff -@@ -1,16 +1,12 @@ - # Test for https://github.com/psf/black/issues/246. - +@@ -1,61 +1,23 @@ +-# Test for https://github.com/psf/black/issues/246. +- 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 - - - # This multiline comments section - # should be split from the statement - # above by two lines. -@@ -19,16 +15,12 @@ - - +-# 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. +-# 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. +-# This comment should be split from the statement above by two lines. class MyClass: pass -@@ -36,7 +28,6 @@ - - some = statement - # This should be stick to the statement above - - - # This should be split from the above by two lines +- + some = statement +-# This should be stick to the statement above +- +- +-# This should be split from the above by two lines class MyClassWithComplexLeadingComments: -@@ -45,16 +36,12 @@ - + pass +- +- class ClassWithDocstring: """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 - - - # leading 1 +-# leading 1 @deco1 # leading 2 -@@ -68,8 +55,6 @@ - - + # leading 2 extra +@@ -65,12 +27,7 @@ + # leading 4 + def decorated(): + pass +- +- some = statement - - - # leading 1 +-# leading 1 @deco1 # leading 2 -@@ -83,8 +68,6 @@ - - + @deco2(with_args=True) +@@ -80,12 +37,7 @@ + # leading 4 + def decorated_with_split_leading_comments(): + pass +- +- some = statement - - - # leading 1 +-# leading 1 @deco1 # leading 2 -@@ -102,11 +85,9 @@ + @deco2(with_args=True) +@@ -95,66 +47,42 @@ + # leading 4 that already has an empty line + def decorated_with_split_leading_comments(): + pass +- +- + def main(): + if a: # Leading comment before inline function def inline(): pass @@ -241,7 +259,10 @@ def bar(): else: # More leading comments def inline_after_else(): -@@ -117,11 +98,9 @@ + pass +- +- + if a: # Leading comment before "top-level inline" function def top_level_quote_inline(): pass @@ -253,64 +274,64 @@ def bar(): else: # More leading comments def top_level_quote_inline_after_else(): -@@ -153,7 +132,6 @@ - # Trailing comment that belongs to this function. - # NOTE this comment only has one empty line below, and the formatter - # should enforce two blank lines. + pass +- +- + class MyClass: + # 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. + def foo(): + pass +- # Trailing comment that belongs to this function +- +- + @decorator1 + @decorator2 # fmt: skip + def bar(): + pass +- +- +-# 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. +- - - @decorator1 # A standalone comment + def bar(): ``` ## Ruff Output ```py -# Test for https://github.com/psf/black/issues/246. - 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. def function(): pass - - 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. class MyClass: pass - - some = statement -# This should be stick to the statement above - -# 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 class MyClassAfterAnotherClassWithDocstring: pass - - some = statement -# leading 1 @deco1 # leading 2 # leading 2 extra @@ -320,10 +341,7 @@ some = statement # leading 4 def decorated(): pass - - some = statement -# leading 1 @deco1 # leading 2 @deco2(with_args=True) @@ -333,10 +351,7 @@ some = statement # leading 4 def decorated_with_split_leading_comments(): pass - - some = statement -# leading 1 @deco1 # leading 2 @deco2(with_args=True) @@ -346,8 +361,6 @@ some = statement # leading 4 that already has an empty line def decorated_with_split_leading_comments(): pass - - def main(): if a: # Leading comment before inline function @@ -360,8 +373,6 @@ def main(): # More leading comments def inline_after_else(): pass - - if a: # Leading comment before "top-level inline" function def top_level_quote_inline(): @@ -373,34 +384,19 @@ else: # 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. def first_method(self): pass - - -# Regression test for https://github.com/psf/black/issues/3454. def foo(): pass - # Trailing comment that belongs to this function - - @decorator1 @decorator2 # fmt: skip def bar(): pass - - -# 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. - @decorator1 # A standalone comment def bar(): diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments_non_breaking_space_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments_non_breaking_space_py.snap index 427a7d5d39..e26a11b3bf 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments_non_breaking_space_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments_non_breaking_space_py.snap @@ -32,7 +32,7 @@ def function(a:int=42): ```diff --- Black +++ Ruff -@@ -1,23 +1,19 @@ +@@ -1,23 +1,16 @@ -from .config import ( - ConfigTypeAttributes, - Int, @@ -41,24 +41,24 @@ def function(a:int=42): +from .config import ( ConfigTypeAttributes, Int, Path, # String, + # DEFAULT_TYPE_ATTRIBUTES, ) - +- -result = 1 # A simple comment -result = (1,) # Another one -+result = 1 # A simple comment -+result = ( 1, ) # Another one - +- -result = 1 #  type: ignore -result = 1 # This comment is talking about type: ignore -square = Square(4) #  type: Optional[Square] -+result = 1 # type: ignore -+result = 1# This comment is talking about type: ignore -+square = Square(4) # type: Optional[Square] - +- - -def function(a: int = 42): - """This docstring is already formatted - a - b ++result = 1 ++result = ( 1, ) ++result = 1 ++result = 1 ++square = Square(4) +def function(a:int=42): + """ This docstring is already formatted + a @@ -76,14 +76,11 @@ def function(a:int=42): from .config import ( ConfigTypeAttributes, Int, Path, # String, # DEFAULT_TYPE_ATTRIBUTES, ) - -result = 1 # A simple comment -result = ( 1, ) # Another one - -result = 1 # type: ignore -result = 1# This comment is talking about type: ignore -square = Square(4) # type: Optional[Square] - +result = 1 +result = ( 1, ) +result = 1 +result = 1 +square = Square(4) def function(a:int=42): """ This docstring is already formatted a diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments_py.snap index b28ec30126..6fa690ac40 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments_py.snap @@ -109,49 +109,103 @@ async def wat(): ```diff --- Black +++ Ruff -@@ -93,4 +93,4 @@ +@@ -1,34 +1,20 @@ +-#!/usr/bin/env python3 +-# fmt: on +-# Some license here. +-# +-# Has many lines. Many, many lines. +-# Many, many, many lines. + """Module docstring. - # Some closing comments. - # Maybe Vim or Emacs directives for formatting. + Possibly also many, many lines. + """ +- + import os.path + import sys +- + import a +-from b.c import X # some noqa comment +- ++from b.c import X + try: + import fast + except ImportError: + import slow as fast +- +- +-# Some comment before a function. + y = 1 + ( + # some strings + y # type: ignore + ) +- +- + def function(default=None): + """Docstring comes first. + +@@ -45,16 +31,7 @@ + + # This return is also commented for some reason. + return default +- +- +-# Explains why we use global state. + GLOBAL_STATE = {"a": a(1), "b": a(2), "c": a(3)} +- +- +-# Another comment! +-# This time two lines. +- +- + class Foo: + """Docstring for class Foo. Example from Sphinx docs.""" + +@@ -73,11 +50,6 @@ + + self.spam = 4 + """Docstring for instance attribute spam.""" +- +- +-#'

This is pweave!

+- +- + @fast(really=True) + async def wat(): + # This comment, for some reason \ +@@ -89,8 +61,3 @@ + print("A OK", file=sys.stdout) + # Comment between things. + print() +- +- +-# Some closing comments. +-# Maybe Vim or Emacs directives for formatting. -# Who knows. \ No newline at end of file -+# Who knows. ``` ## Ruff Output ```py -#!/usr/bin/env python3 -# fmt: on -# Some license here. -# -# Has many lines. Many, many lines. -# Many, many, many lines. """Module docstring. Possibly also many, many lines. """ - import os.path import sys - import a -from b.c import X # some noqa comment - +from b.c import X try: import fast except ImportError: import slow as fast - - -# Some comment before a function. y = 1 ( # some strings y # type: ignore ) - - def function(default=None): """Docstring comes first. @@ -168,16 +222,7 @@ def function(default=None): # This return is also commented for some reason. return default - - -# Explains why we use global state. GLOBAL_STATE = {"a": a(1), "b": a(2), "c": a(3)} - - -# Another comment! -# This time two lines. - - class Foo: """Docstring for class Foo. Example from Sphinx docs.""" @@ -196,11 +241,6 @@ class Foo: self.spam = 4 """Docstring for instance attribute spam.""" - - -#'

This is pweave!

- - @fast(really=True) async def wat(): # This comment, for some reason \ @@ -212,11 +252,6 @@ async def wat(): print("A OK", file=sys.stdout) # Comment between things. print() - - -# Some closing comments. -# Maybe Vim or Emacs directives for formatting. -# Who knows. ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_no_extra_empty_line_before_eof_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_no_extra_empty_line_before_eof_py.snap new file mode 100644 index 0000000000..b69664d3c1 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_no_extra_empty_line_before_eof_py.snap @@ -0,0 +1,43 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring_no_extra_empty_line_before_eof.py +--- +## Input + +```py +# Make sure when the file ends with class's docstring, +# It doesn't add extra blank lines. +class ClassWithDocstring: + """A docstring.""" +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,4 +1,2 @@ +-# Make sure when the file ends with class's docstring, +-# It doesn't add extra blank lines. + class ClassWithDocstring: + """A docstring.""" +``` + +## Ruff Output + +```py +class ClassWithDocstring: + """A docstring.""" +``` + +## Black Output + +```py +# Make sure when the file ends with class's docstring, +# It doesn't add extra blank lines. +class ClassWithDocstring: + """A docstring.""" +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_preview_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_preview_py.snap index b95e94cf32..3a06521d11 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_preview_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_preview_py.snap @@ -63,23 +63,57 @@ def single_quote_docstring_over_line_limit2(): ```diff --- Black +++ Ruff -@@ -1,9 +1,11 @@ +@@ -1,48 +1,32 @@ def docstring_almost_at_line_limit(): - """long docstring.................................................................""" +- +- + """long docstring................................................................. + """ - - def docstring_almost_at_line_limit_with_prefix(): - f"""long docstring................................................................""" +- +- + f"""long docstring................................................................ + """ - - def mulitline_docstring_almost_at_line_limit(): -@@ -45,4 +47,4 @@ + """long docstring................................................................. + .................................................................................. + """ +- +- + def mulitline_docstring_almost_at_line_limit_with_prefix(): + f"""long docstring................................................................ + .................................................................................. + """ +- +- + def docstring_at_line_limit(): + """long docstring................................................................""" +- +- + def docstring_at_line_limit_with_prefix(): + f"""long docstring...............................................................""" +- +- + def multiline_docstring_at_line_limit(): + """first line----------------------------------------------------------------------- + + second line----------------------------------------------------------------------""" +- +- + def multiline_docstring_at_line_limit_with_prefix(): + f"""first line---------------------------------------------------------------------- + + second line----------------------------------------------------------------------""" +- +- + def single_quote_docstring_over_line_limit(): + "We do not want to put the closing quote on a new line as that is invalid (see GH-3141)." +- +- def single_quote_docstring_over_line_limit2(): - "We do not want to put the closing quote on a new line as that is invalid (see GH-3141)." + 'We do not want to put the closing quote on a new line as that is invalid (see GH-3141).' @@ -91,51 +125,33 @@ def single_quote_docstring_over_line_limit2(): def docstring_almost_at_line_limit(): """long docstring................................................................. """ - - def docstring_almost_at_line_limit_with_prefix(): f"""long docstring................................................................ """ - - def mulitline_docstring_almost_at_line_limit(): """long docstring................................................................. .................................................................................. """ - - def mulitline_docstring_almost_at_line_limit_with_prefix(): f"""long docstring................................................................ .................................................................................. """ - - def docstring_at_line_limit(): """long docstring................................................................""" - - def docstring_at_line_limit_with_prefix(): f"""long docstring...............................................................""" - - def multiline_docstring_at_line_limit(): """first line----------------------------------------------------------------------- second line----------------------------------------------------------------------""" - - def multiline_docstring_at_line_limit_with_prefix(): f"""first line---------------------------------------------------------------------- second line----------------------------------------------------------------------""" - - def single_quote_docstring_over_line_limit(): "We do not want to put the closing quote on a new line as that is invalid (see GH-3141)." - - def single_quote_docstring_over_line_limit2(): 'We do not want to put the closing quote on a new line as that is invalid (see GH-3141).' ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_py.snap index 211c0527f1..3bfe82482c 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_py.snap @@ -234,7 +234,7 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): ```diff --- Black +++ Ruff -@@ -1,83 +1,84 @@ +@@ -1,219 +1,155 @@ class MyClass: + """ Multiline + class docstring @@ -251,51 +251,51 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): - method docstring - """ - pass +- +- + pass - - def foo(): - """This is a docstring with - some lines of text here - """ - return +- +- + """This is a docstring with + some lines of text here + """ + return - - def bar(): - """This is another docstring - with more lines of text - """ - return +- +- + '''This is another docstring + with more lines of text + ''' + return - - def baz(): - '''"This" is a string with some - embedded "quotes"''' - return +- +- + '''"This" is a string with some + embedded "quotes"''' + return - - def troz(): - """Indentation with tabs - is just as OK - """ - return +- +- + '''Indentation with tabs + is just as OK + ''' + return - - def zort(): - """Another - multiline @@ -303,12 +303,12 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): - """ - pass - +- + """Another + multiline + docstring + """ + pass - def poit(): - """ - Lorem ipsum dolor sit amet. @@ -323,6 +323,8 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): - - aliquip ex ea commodo consequat - """ - pass +- +- + Consectetur adipiscing elit: + - sed do eiusmod tempor incididunt ut labore + - dolore magna aliqua @@ -331,107 +333,107 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): + - aliquip ex ea commodo consequat + """ + pass - - def under_indent(): - """ - These lines are indented in a way that does not - make sense. -- """ -- pass + """ + These lines are indented in a way that does not +make sense. + """ + pass - - - def over_indent(): ++def over_indent(): + """ + This has a shallow indent + - But some lines are deeper + - And the closing quote is too deep """ +- pass +- ++ pass ++def single_line(): ++ """But with a newline after it! + +-def over_indent(): +- """ - This has a shallow indent - - But some lines are deeper - - And the closing quote is too deep -- """ -- pass -+ pass - - - def single_line(): -- """But with a newline after it!""" -+ """But with a newline after it! -+ -+ """ + """ pass - - -@@ -88,25 +89,30 @@ - - +- +- +-def single_line(): +- """But with a newline after it!""" +- pass +- +- + def this(): + r""" + 'hey ho' + """ +- +- def that(): - """ "hey yah" """ +- +- + """ "hey yah" """ - - def and_that(): - """ - "hey yah" """ +- +- + """ + "hey yah" """ - - def and_this(): - ''' - "hey yah"''' +- +- + ''' + "hey yah"''' - - def multiline_whitespace(): - """ """ +- +- + ''' + + + + + ''' - - def oneline_whitespace(): - """ """ +- +- + ''' ''' - - def empty(): -@@ -114,46 +120,43 @@ - - + """""" +- +- def single_quotes(): - "testing" -+ 'testing' - - +- +- -def believe_it_or_not_this_is_in_the_py_stdlib(): - ''' - "hey yah"''' +- +- ++ 'testing' +def believe_it_or_not_this_is_in_the_py_stdlib(): ''' +"hey yah"''' - - def ignored_docstring(): """a => \ --b""" + b""" +- - -+b""" - def single_line_docstring_with_whitespace(): - """This should be stripped""" - +- + """ This should be stripped """ - def docstring_with_inline_tabs_and_space_indentation(): """hey @@ -447,20 +449,22 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): + + line ends with some tabs """ - - +- +- def docstring_with_inline_tabs_and_tab_indentation(): - """hey -+ """hey - +- - tab separated value - tab at start of line and then a tab separated value - multiple tabs at the beginning and inline - mixed tabs and spaces at beginning. next line has mixed tabs and spaces only. -- ++ """hey + - line ends with some tabs - """ - pass +- +- + tab separated value + tab at start of line and then a tab separated value + multiple tabs at the beginning and inline @@ -469,53 +473,69 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): + line ends with some tabs + """ + pass - - def backslash_space(): -@@ -161,16 +164,15 @@ - - + """\ """ +- +- def multiline_backslash_1(): - """ + ''' hey\there\ - \ """ +- +- + \ ''' - - def multiline_backslash_2(): - """ - hey there \ """ +- +- +-# Regression test for #3425 + ''' + hey there \ ''' - -- - # Regression test for #3425 def multiline_backslash_really_long_dont_crash(): """ -@@ -178,8 +180,8 @@ - - + hey there hello guten tag hi hoow are you ola zdravstvuyte ciao como estas ca va \ """ +- +- def multiline_backslash_3(): - """ - already escaped \\""" +- +- + ''' + already escaped \\ ''' - - def my_god_its_full_of_stars_1(): -@@ -188,7 +190,7 @@ - - # the space below is actually a \u2001, removed in output + "I'm sorry Dave\u2001" +- +- +-# the space below is actually a \u2001, removed in output def my_god_its_full_of_stars_2(): - "I'm sorry Dave" +- +- + "I'm sorry Dave " - - def docstring_almost_at_line_limit(): -@@ -213,7 +215,7 @@ + """long docstring.................................................................""" +- +- + def docstring_almost_at_line_limit2(): + """long docstring................................................................. + .................................................................................. + """ +- +- + def docstring_at_line_limit(): + """long docstring................................................................""" +- +- + def multiline_docstring_at_line_limit(): + """first line----------------------------------------------------------------------- + second line----------------------------------------------------------------------""" +- +- def stable_quote_normalization_with_immediate_inner_single_quote(self): - """' + '''' @@ -538,42 +558,31 @@ class MyClass: method docstring """ pass - - def foo(): """This is a docstring with some lines of text here """ return - - def bar(): '''This is another docstring with more lines of text ''' return - - def baz(): '''"This" is a string with some embedded "quotes"''' return - - def troz(): '''Indentation with tabs is just as OK ''' return - - def zort(): """Another multiline docstring """ pass - def poit(): """ Lorem ipsum dolor sit amet. @@ -586,16 +595,12 @@ def poit(): - aliquip ex ea commodo consequat """ pass - - def under_indent(): """ These lines are indented in a way that does not make sense. """ pass - - def over_indent(): """ This has a shallow indent @@ -603,35 +608,23 @@ def over_indent(): - And the closing quote is too deep """ pass - - def single_line(): """But with a newline after it! """ pass - - def this(): r""" 'hey ho' """ - - def that(): """ "hey yah" """ - - def and_that(): """ "hey yah" """ - - def and_this(): ''' "hey yah"''' - - def multiline_whitespace(): ''' @@ -639,31 +632,19 @@ def multiline_whitespace(): ''' - - def oneline_whitespace(): ''' ''' - - def empty(): """""" - - def single_quotes(): 'testing' - - def believe_it_or_not_this_is_in_the_py_stdlib(): ''' "hey yah"''' - - def ignored_docstring(): """a => \ -b""" - +b""" def single_line_docstring_with_whitespace(): """ This should be stripped """ - def docstring_with_inline_tabs_and_space_indentation(): """hey @@ -674,8 +655,6 @@ def docstring_with_inline_tabs_and_space_indentation(): line ends with some tabs """ - - def docstring_with_inline_tabs_and_tab_indentation(): """hey @@ -687,63 +666,38 @@ def docstring_with_inline_tabs_and_tab_indentation(): line ends with some tabs """ pass - - def backslash_space(): """\ """ - - def multiline_backslash_1(): ''' hey\there\ \ ''' - - def multiline_backslash_2(): ''' hey there \ ''' - -# Regression test for #3425 def multiline_backslash_really_long_dont_crash(): """ hey there hello guten tag hi hoow are you ola zdravstvuyte ciao como estas ca va \ """ - - def multiline_backslash_3(): ''' already escaped \\ ''' - - def my_god_its_full_of_stars_1(): "I'm sorry Dave\u2001" - - -# the space below is actually a \u2001, removed in output def my_god_its_full_of_stars_2(): "I'm sorry Dave " - - def docstring_almost_at_line_limit(): """long docstring.................................................................""" - - def docstring_almost_at_line_limit2(): """long docstring................................................................. .................................................................................. """ - - def docstring_at_line_limit(): """long docstring................................................................""" - - def multiline_docstring_at_line_limit(): """first line----------------------------------------------------------------------- second line----------------------------------------------------------------------""" - - def stable_quote_normalization_with_immediate_inner_single_quote(self): '''' diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__empty_lines_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__empty_lines_py.snap index 7e14fea9d2..26b6b118bb 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__empty_lines_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__empty_lines_py.snap @@ -105,9 +105,11 @@ def g(): ```diff --- Black +++ Ruff -@@ -3,9 +3,9 @@ - - # leading comment +@@ -1,11 +1,8 @@ + """Docstring.""" +- +- +-# leading comment def f(): - NO = "" - SPACE = " " @@ -118,7 +120,7 @@ def g(): t = leaf.type p = leaf.parent # trailing comment -@@ -16,14 +16,19 @@ +@@ -16,14 +13,19 @@ if t == token.COMMENT: # another trailing comment return DOUBLESPACE @@ -138,16 +140,17 @@ def g(): if prevp.type == token.EQUAL: if prevp.parent and prevp.parent.type in { syms.typedargslist, -@@ -44,16 +49,14 @@ +@@ -43,17 +45,10 @@ + syms.dictsetmaker, }: return NO - - - ############################################################################### - # SECTION BECAUSE SECTIONS - ############################################################################### - - +-############################################################################### +-# SECTION BECAUSE SECTIONS +-############################################################################### +- +- def g(): - NO = "" - SPACE = " " @@ -158,7 +161,7 @@ def g(): t = leaf.type p = leaf.parent -@@ -67,7 +70,7 @@ +@@ -67,7 +62,7 @@ return DOUBLESPACE # Another comment because more comments @@ -173,9 +176,6 @@ def g(): ```py """Docstring.""" - - -# leading comment def f(): NO = '' SPACE = ' ' @@ -222,11 +222,6 @@ def f(): syms.dictsetmaker, }: return NO - -############################################################################### -# SECTION BECAUSE SECTIONS -############################################################################### - def g(): NO = '' SPACE = ' ' diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap index e6606c57c8..f6b997b73c 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap @@ -401,7 +401,7 @@ last_call() +call(kwarg='hey') +call(arg, kwarg='hey') +call(arg, another, kwarg='hey', **kwargs) -+call(this_is_a_very_long_variable_which_will_force_a_delimiter_split, arg, another, kwarg='hey', **kwargs) # note: no trailing comma pre-3.6 ++call(this_is_a_very_long_variable_which_will_force_a_delimiter_split, arg, another, kwarg='hey', **kwargs) call(*gidgets[:2]) call(a, *gidgets[:2]) call(**self.screen_kwargs) @@ -437,7 +437,7 @@ last_call() -) # type: ignore +xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[ + ..., List[SomeClass] -+] = classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) # type: ignore ++] = classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) slice[0] slice[0:1] slice[0:1:2] @@ -510,22 +510,22 @@ last_call() Ø = set() authors.łukasz.say_thanks() mapping = { -@@ -233,138 +170,84 @@ +@@ -233,138 +170,82 @@ C: 0.1 * (10.0 / 12), D: 0.1 * (10.0 / 12), } - - +- def gen(): yield from outside_of_generator - a = yield - b = yield - c = yield - +- + a = (yield) + b = ((yield)) + c = (((yield))) - async def f(): await some.complicated[0].call(with_args=(True or (1 is not 1))) - @@ -773,7 +773,7 @@ call(arg) call(kwarg='hey') call(arg, kwarg='hey') call(arg, another, kwarg='hey', **kwargs) -call(this_is_a_very_long_variable_which_will_force_a_delimiter_split, arg, another, kwarg='hey', **kwargs) # note: no trailing comma pre-3.6 +call(this_is_a_very_long_variable_which_will_force_a_delimiter_split, arg, another, kwarg='hey', **kwargs) call(*gidgets[:2]) call(a, *gidgets[:2]) call(**self.screen_kwargs) @@ -801,7 +801,7 @@ xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # t ) xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[ ..., List[SomeClass] -] = classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) # type: ignore +] = classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) slice[0] slice[0:1] slice[0:1:2] @@ -860,13 +860,11 @@ mapping = { C: 0.1 * (10.0 / 12), D: 0.1 * (10.0 / 12), } - def gen(): yield from outside_of_generator a = (yield) b = ((yield)) c = (((yield))) - async def f(): await some.complicated[0].call(with_args=(True or (1 is not 1))) print(* [] or [1]) diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff2_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff2_py.snap new file mode 100644 index 0000000000..ab14d29ecd --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff2_py.snap @@ -0,0 +1,174 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff2.py +--- +## Input + +```py +import pytest + +TmSt = 1 +TmEx = 2 + +# fmt: off + +# Test data: +# Position, Volume, State, TmSt/TmEx/None, [call, [arg1...]] + +@pytest.mark.parametrize('test', [ + + # Test don't manage the volume + [ + ('stuff', 'in') + ], +]) +def test_fader(test): + pass + +def check_fader(test): + + pass + +def verify_fader(test): + # misaligned comment + pass + +def verify_fader(test): + """Hey, ho.""" + assert test.passed() + +def test_calculate_fades(): + calcs = [ + # one is zero/none + (0, 4, 0, 0, 10, 0, 0, 6, 10), + (None, 4, 0, 0, 10, 0, 0, 6, 10), + ] + +# fmt: on +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,13 +1,6 @@ + import pytest +- + TmSt = 1 + TmEx = 2 +- +-# fmt: off +- +-# Test data: +-# Position, Volume, State, TmSt/TmEx/None, [call, [arg1...]] +- + @pytest.mark.parametrize('test', [ + + # Test don't manage the volume +@@ -17,24 +10,18 @@ + ]) + def test_fader(test): + pass +- + def check_fader(test): + + pass +- + def verify_fader(test): + # misaligned comment + pass +- + def verify_fader(test): + """Hey, ho.""" + assert test.passed() +- + def test_calculate_fades(): + calcs = [ + # one is zero/none + (0, 4, 0, 0, 10, 0, 0, 6, 10), + (None, 4, 0, 0, 10, 0, 0, 6, 10), + ] +- +-# fmt: on +``` + +## Ruff Output + +```py +import pytest +TmSt = 1 +TmEx = 2 +@pytest.mark.parametrize('test', [ + + # Test don't manage the volume + [ + ('stuff', 'in') + ], +]) +def test_fader(test): + pass +def check_fader(test): + + pass +def verify_fader(test): + # misaligned comment + pass +def verify_fader(test): + """Hey, ho.""" + assert test.passed() +def test_calculate_fades(): + calcs = [ + # one is zero/none + (0, 4, 0, 0, 10, 0, 0, 6, 10), + (None, 4, 0, 0, 10, 0, 0, 6, 10), + ] +``` + +## Black Output + +```py +import pytest + +TmSt = 1 +TmEx = 2 + +# fmt: off + +# Test data: +# Position, Volume, State, TmSt/TmEx/None, [call, [arg1...]] + +@pytest.mark.parametrize('test', [ + + # Test don't manage the volume + [ + ('stuff', 'in') + ], +]) +def test_fader(test): + pass + +def check_fader(test): + + pass + +def verify_fader(test): + # misaligned comment + pass + +def verify_fader(test): + """Hey, ho.""" + assert test.passed() + +def test_calculate_fades(): + calcs = [ + # one is zero/none + (0, 4, 0, 0, 10, 0, 0, 6, 10), + (None, 4, 0, 0, 10, 0, 0, 6, 10), + ] + +# fmt: on +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff3_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff3_py.snap index 772a2828ad..c40098aa52 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff3_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff3_py.snap @@ -30,10 +30,21 @@ x = [ ```diff --- Black +++ Ruff -@@ -12,4 +12,6 @@ +@@ -1,15 +1,11 @@ +-# fmt: off + x = [ + 1, 2, + 3, 4, ] - # fmt: on - +-# fmt: on +- +-# fmt: off + x = [ + 1, 2, + 3, 4, + ] +-# fmt: on +- -x = [1, 2, 3, 4] +x = [ + 1, 2, 3, 4 @@ -43,20 +54,14 @@ x = [ ## Ruff Output ```py -# fmt: off x = [ 1, 2, 3, 4, ] -# fmt: on - -# fmt: off x = [ 1, 2, 3, 4, ] -# fmt: on - x = [ 1, 2, 3, 4 ] diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff4_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff4_py.snap index cda65520cf..ef85726a31 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff4_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff4_py.snap @@ -26,15 +26,17 @@ def f(): pass ```diff --- Black +++ Ruff -@@ -4,17 +4,10 @@ +@@ -1,20 +1,11 @@ +-# fmt: off + @test([ + 1, 2, 3, 4, ]) # fmt: on -def f(): - pass - -+def f(): pass - +- -@test( - [ - 1, @@ -45,6 +47,7 @@ def f(): pass -) -def f(): - pass ++def f(): pass +@test([ + 1, 2, + 3, 4, @@ -55,14 +58,12 @@ def f(): pass ## Ruff Output ```py -# fmt: off @test([ 1, 2, 3, 4, ]) # fmt: on def f(): pass - @test([ 1, 2, 3, 4, diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap index 08c5082263..73135d6d22 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap @@ -97,38 +97,78 @@ elif unformatted: ```diff --- Black +++ Ruff -@@ -44,7 +44,7 @@ +@@ -1,4 +1,3 @@ +-# Regression test for https://github.com/psf/black/issues/3129. + setup( + entry_points={ + # fmt: off +@@ -9,9 +8,6 @@ + ] # Includes an formatted indentation. + }, + ) +- +- +-# Regression test for https://github.com/psf/black/issues/2015. + run( + # fmt: off + [ +@@ -22,9 +18,6 @@ + + path, + check=True, + ) +- +- +-# Regression test for https://github.com/psf/black/issues/3026. + def test_func(): + # yapf: disable + if unformatted( args ): +@@ -34,9 +27,6 @@ + return True + + return False +- +- +-# Regression test for https://github.com/psf/black/issues/2567. + if True: + # fmt: off + for _ in range( 1 ): +@@ -44,10 +34,7 @@ print ( "This won't be formatted" ) print ( "This won't be formatted either" ) else: - print("This will be formatted") +- +- +-# Regression test for https://github.com/psf/black/issues/3184. + print ( "This will be formatted" ) - - - # Regression test for https://github.com/psf/black/issues/3184. -@@ -61,7 +61,7 @@ + class A: + async def call(param): + if param: +@@ -61,27 +48,16 @@ elif param[0:4] in ("ZZZZ",): print ( "This won't be formatted either" ) - print("This will be formatted") +- +- +-# Regression test for https://github.com/psf/black/issues/2985. + print ( "This will be formatted" ) - - - # Regression test for https://github.com/psf/black/issues/2985. -@@ -70,11 +70,8 @@ + class Named(t.Protocol): + # fmt: off @property def this_wont_be_formatted ( self ) -> str: ... - +- - class Factory(t.Protocol): - def this_will_be_formatted(self, **kwargs) -> Named: - ... - +- # fmt: on +- +- +-# Regression test for https://github.com/psf/black/issues/3436. + def this_will_be_formatted ( self, **kwargs ) -> Named: ... - # fmt: on - - -@@ -83,5 +80,5 @@ + if x: return x # fmt: off elif unformatted: @@ -141,7 +181,6 @@ elif unformatted: ## Ruff Output ```py -# Regression test for https://github.com/psf/black/issues/3129. setup( entry_points={ # fmt: off @@ -152,9 +191,6 @@ setup( ] # Includes an formatted indentation. }, ) - - -# Regression test for https://github.com/psf/black/issues/2015. run( # fmt: off [ @@ -165,9 +201,6 @@ run( + path, check=True, ) - - -# Regression test for https://github.com/psf/black/issues/3026. def test_func(): # yapf: disable if unformatted( args ): @@ -177,9 +210,6 @@ def test_func(): return True return False - - -# Regression test for https://github.com/psf/black/issues/2567. if True: # fmt: off for _ in range( 1 ): @@ -188,9 +218,6 @@ if True: print ( "This won't be formatted either" ) else: print ( "This will be formatted" ) - - -# Regression test for https://github.com/psf/black/issues/3184. class A: async def call(param): if param: @@ -205,20 +232,12 @@ class A: print ( "This won't be formatted either" ) print ( "This will be formatted" ) - - -# Regression test for https://github.com/psf/black/issues/2985. class Named(t.Protocol): # fmt: off @property def this_wont_be_formatted ( self ) -> str: ... - class Factory(t.Protocol): def this_will_be_formatted ( self, **kwargs ) -> Named: ... - # fmt: on - - -# Regression test for https://github.com/psf/black/issues/3436. if x: return x # fmt: off diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff_py.snap index 2bace21cc8..fb6ecc96c4 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff_py.snap @@ -199,32 +199,37 @@ d={'a':1, ```diff --- Black +++ Ruff -@@ -4,18 +4,17 @@ - +@@ -1,22 +1,11 @@ +-#!/usr/bin/env python3 + import asyncio + import sys +- from third_party import X, Y, Z - +- -from library import some_connection, some_decorator - +-# fmt: off +from library import some_connection, \ + some_decorator - # fmt: off from third_party import (X, Y, Z) - # fmt: on +-# fmt: on -f"trigger 3.6 mode" -+f'trigger 3.6 mode' - # Comment 1 - - # Comment 2 - +-# Comment 1 - - # fmt: off +-# Comment 2 +- +- +-# fmt: off ++f'trigger 3.6 mode' def func_no_args(): a; b; c -@@ -39,34 +38,16 @@ + if True: raise RuntimeError +@@ -38,73 +27,41 @@ + ) def function_signature_stress_test(number:int,no_annotation=None,text:str='default',* ,debug:bool=False,**kwargs) -> str: return text[number:-1] - # fmt: on +-# fmt: on -def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""): - offset = attr.ib(default=attr.Factory(lambda: _r.uniform(1, 2))) - assert task._cancel_stack[: len(old_stack)] == old_stack @@ -259,10 +264,10 @@ d={'a':1, key: 'value', } - - +- def subscriptlist(): atom[ -@@ -74,17 +55,14 @@ + # fmt: off 'some big and', 'complex subscript', # fmt: on @@ -270,22 +275,22 @@ d={'a':1, - andhere, - ] - +- + goes + here, andhere, + ] - def import_as_names(): # fmt: off from hello import a, b 'unformatted' - # fmt: on +- # fmt: on +- - - def testlist_star_expr(): # fmt: off -@@ -92,18 +70,16 @@ + a , b = *hello 'unformatted' - # fmt: on - +- # fmt: on +- - def yield_expr(): # fmt: off @@ -297,43 +302,46 @@ d={'a':1, # fmt: off ( yield hello ) 'unformatted' - # fmt: on +- # fmt: on +- - - def example(session): # fmt: off -@@ -114,8 +90,6 @@ + result = session\ +@@ -113,9 +70,6 @@ + models.Customer.email == email_address)\ .order_by(models.Customer.id.asc())\ .all() - # fmt: on +- # fmt: on - - def off_and_on_without_data(): """All comments here are technically on the same prefix. -@@ -123,12 +97,12 @@ +@@ -123,12 +77,12 @@ """ # fmt: off - # hey, that won't work +- # fmt: on +- pass + #hey, that won't work -+ -+ - # fmt: on - pass -- -- + + ++ # fmt: on ++ pass def on_and_off_broken(): """Another known limitation.""" # fmt: on -@@ -139,19 +113,12 @@ +@@ -137,21 +91,10 @@ + and_=indeed . it is not formatted + because . the . handling . inside . generate_ignored_nodes() now . considers . multiple . fmt . directives . within . one . prefix - # fmt: on - # fmt: off +- # fmt: on +- # fmt: off - # ...but comments still get reformatted even though they should not be -+ # ...but comments still get reformatted even though they should not be - # fmt: on +- # fmt: on - - def long_lines(): @@ -349,7 +357,7 @@ d={'a':1, ) # fmt: off a = ( -@@ -182,24 +149,19 @@ +@@ -182,24 +125,19 @@ re.MULTILINE|re.VERBOSE # fmt: on ) @@ -364,7 +372,7 @@ d={'a':1, + (1, 2, 3, 4), + (5, 6, 7, 8), + (9, 10, 11, 12) -+ } # yapf: disable ++ } cfg.rule( - "Default", - "address", @@ -383,38 +391,32 @@ d={'a':1, # fmt: off xxxxxxx_xxxxxxxxxxxx={ "xxxxxxxx": { -@@ -214,7 +176,7 @@ +@@ -214,11 +152,9 @@ }, }, # fmt: on - xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5, + xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5 ) - # fmt: off +-# fmt: off yield 'hello' +-# No formatting to the end of the file + l=[1,2,3] + d={'a':1, + 'b':2} ``` ## Ruff Output ```py -#!/usr/bin/env python3 import asyncio import sys - from third_party import X, Y, Z - from library import some_connection, \ some_decorator -# fmt: off from third_party import (X, Y, Z) -# fmt: on f'trigger 3.6 mode' -# Comment 1 - -# Comment 2 - -# fmt: off def func_no_args(): a; b; c if True: raise RuntimeError @@ -436,7 +438,6 @@ many_args=[1,2,3] ) def function_signature_stress_test(number:int,no_annotation=None,text:str='default',* ,debug:bool=False,**kwargs) -> str: return text[number:-1] -# fmt: on def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r''): offset = attr.ib(default=attr.Factory( lambda: _r.uniform(1, 2))) assert task._cancel_stack[:len(old_stack)] == old_stack @@ -447,7 +448,6 @@ something = { # fmt: off key: 'value', } - def subscriptlist(): atom[ # fmt: off @@ -456,19 +456,14 @@ def subscriptlist(): # fmt: on goes + here, andhere, ] - def import_as_names(): # fmt: off from hello import a, b 'unformatted' - # fmt: on - def testlist_star_expr(): # fmt: off a , b = *hello 'unformatted' - # fmt: on - def yield_expr(): # fmt: off yield hello @@ -478,8 +473,6 @@ def yield_expr(): # fmt: off ( yield hello ) 'unformatted' - # fmt: on - def example(session): # fmt: off result = session\ @@ -488,7 +481,6 @@ def example(session): models.Customer.email == email_address)\ .order_by(models.Customer.id.asc())\ .all() - # fmt: on def off_and_on_without_data(): """All comments here are technically on the same prefix. @@ -510,10 +502,6 @@ def on_and_off_broken(): and_=indeed . it is not formatted because . the . handling . inside . generate_ignored_nodes() now . considers . multiple . fmt . directives . within . one . prefix - # fmt: on - # fmt: off - # ...but comments still get reformatted even though they should not be - # fmt: on def long_lines(): if True: typedargslist.extend( @@ -554,7 +542,7 @@ def single_literal_yapf_disable(): (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12) - } # yapf: disable + } cfg.rule( "Default", "address", xxxx_xxxx=["xxx-xxxxxx-xxxxxxxxxx"], @@ -577,9 +565,7 @@ cfg.rule( # fmt: on xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5 ) -# fmt: off yield 'hello' -# No formatting to the end of the file l=[1,2,3] d={'a':1, 'b':2} diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip2_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip2_py.snap index 99d137293e..700d4d6688 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip2_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip2_py.snap @@ -22,13 +22,14 @@ l3 = ["I have", "trailing comma", "so I should be braked",] - "into multiple lines", - "because it is way too long", -] -+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 +-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", -] ++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"] +l3 = ["I have", "trailing comma", "so I should be braked",] ``` @@ -36,7 +37,7 @@ l3 = ["I have", "trailing comma", "so I should be braked",] ```py 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 +l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"] l3 = ["I have", "trailing comma", "so I should be braked",] ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip3_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip3_py.snap index 6dd878bc1e..9fa822ada8 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip3_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip3_py.snap @@ -20,14 +20,15 @@ f = ["This is a very long line that should be formatted into a clearer line ", " ```diff --- Black +++ Ruff -@@ -1,10 +1,7 @@ +@@ -1,10 +1,5 @@ -a = 3 +-# fmt: off +a = 3 - # fmt: off b, c = 1, 2 - d = 6 # fmt: skip +-d = 6 # fmt: skip ++d = 6 e = 5 - # fmt: on +-# fmt: on -f = [ - "This is a very long line that should be formatted into a clearer line ", - "by rearranging.", @@ -39,11 +40,9 @@ f = ["This is a very long line that should be formatted into a clearer line ", " ```py a = 3 -# fmt: off b, c = 1, 2 -d = 6 # fmt: skip +d = 6 e = 5 -# fmt: on f = ["This is a very long line that should be formatted into a clearer line ", "by rearranging."] ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip4_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip4_py.snap index 745448d84d..a303ddfc50 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip4_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip4_py.snap @@ -16,15 +16,15 @@ l = [1, 2, 3,] ```diff --- Black +++ Ruff -@@ -1,7 +1,3 @@ +@@ -1,7 +1,2 @@ -a = 2 -+a = 2 - # fmt: skip +-# fmt: skip -l = [ - 1, - 2, - 3, -] ++a = 2 +l = [1, 2, 3,] ``` @@ -32,7 +32,6 @@ l = [1, 2, 3,] ```py a = 2 -# fmt: skip l = [1, 2, 3,] ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip6_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip6_py.snap new file mode 100644 index 0000000000..46b8920531 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip6_py.snap @@ -0,0 +1,49 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip6.py +--- +## Input + +```py +class A: + def f(self): + for line in range(10): + if True: + pass # fmt: skip +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -2,4 +2,4 @@ + def f(self): + for line in range(10): + if True: +- pass # fmt: skip ++ pass +``` + +## Ruff Output + +```py +class A: + def f(self): + for line in range(10): + if True: + pass +``` + +## Black Output + +```py +class A: + def f(self): + for line in range(10): + if True: + pass # fmt: skip +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip7_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip7_py.snap index e9d2293265..bda5c38d73 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip7_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip7_py.snap @@ -23,18 +23,18 @@ d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasu -c = 9 # fmt: skip -d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" # fmt:skip +a = "this is some code" -+b = 5 #fmt:skip -+c = 9 #fmt: skip -+d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" #fmt:skip ++b = 5 ++c = 9 ++d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" ``` ## Ruff Output ```py a = "this is some code" -b = 5 #fmt:skip -c = 9 #fmt: skip -d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" #fmt:skip +b = 5 +c = 9 +d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip8_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip8_py.snap new file mode 100644 index 0000000000..adc5c6fa78 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip8_py.snap @@ -0,0 +1,253 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip8.py +--- +## Input + +```py +# Make sure a leading comment is not removed. +def some_func( unformatted, args ): # fmt: skip + print("I am some_func") + return 0 + # Make sure this comment is not removed. + + +# Make sure a leading comment is not removed. +async def some_async_func( unformatted, args): # fmt: skip + print("I am some_async_func") + await asyncio.sleep(1) + + +# Make sure a leading comment is not removed. +class SomeClass( Unformatted, SuperClasses ): # fmt: skip + def some_method( self, unformatted, args ): # fmt: skip + print("I am some_method") + return 0 + + async def some_async_method( self, unformatted, args ): # fmt: skip + print("I am some_async_method") + await asyncio.sleep(1) + + +# Make sure a leading comment is not removed. +if unformatted_call( args ): # fmt: skip + print("First branch") + # Make sure this is not removed. +elif another_unformatted_call( args ): # fmt: skip + print("Second branch") +else : # fmt: skip + print("Last branch") + + +while some_condition( unformatted, args ): # fmt: skip + print("Do something") + + +for i in some_iter( unformatted, args ): # fmt: skip + print("Do something") + + +async def test_async_for(): + async for i in some_async_iter( unformatted, args ): # fmt: skip + print("Do something") + + +try : # fmt: skip + some_call() +except UnformattedError as ex: # fmt: skip + handle_exception() +finally : # fmt: skip + finally_call() + + +with give_me_context( unformatted, args ): # fmt: skip + print("Do something") + + +async def test_async_with(): + async with give_me_async_context( unformatted, args ): # fmt: skip + print("Do something") +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,17 +1,9 @@ +-# Make sure a leading comment is not removed. + def some_func( unformatted, args ): # fmt: skip + print("I am some_func") + return 0 +- # Make sure this comment is not removed. +- +- +-# Make sure a leading comment is not removed. + async def some_async_func( unformatted, args): # fmt: skip + print("I am some_async_func") + await asyncio.sleep(1) +- +- +-# Make sure a leading comment is not removed. + class SomeClass( Unformatted, SuperClasses ): # fmt: skip + def some_method( self, unformatted, args ): # fmt: skip + print("I am some_method") +@@ -20,9 +12,6 @@ + async def some_async_method( self, unformatted, args ): # fmt: skip + print("I am some_async_method") + await asyncio.sleep(1) +- +- +-# Make sure a leading comment is not removed. + if unformatted_call( args ): # fmt: skip + print("First branch") + # Make sure this is not removed. +@@ -30,33 +19,21 @@ + print("Second branch") + else : # fmt: skip + print("Last branch") +- +- + while some_condition( unformatted, args ): # fmt: skip + print("Do something") +- +- + for i in some_iter( unformatted, args ): # fmt: skip + print("Do something") +- +- + async def test_async_for(): + async for i in some_async_iter( unformatted, args ): # fmt: skip + print("Do something") +- +- + try : # fmt: skip + some_call() + except UnformattedError as ex: # fmt: skip + handle_exception() + finally : # fmt: skip + finally_call() +- +- + with give_me_context( unformatted, args ): # fmt: skip + print("Do something") +- +- + async def test_async_with(): + async with give_me_async_context( unformatted, args ): # fmt: skip + print("Do something") +``` + +## Ruff Output + +```py +def some_func( unformatted, args ): # fmt: skip + print("I am some_func") + return 0 +async def some_async_func( unformatted, args): # fmt: skip + print("I am some_async_func") + await asyncio.sleep(1) +class SomeClass( Unformatted, SuperClasses ): # fmt: skip + def some_method( self, unformatted, args ): # fmt: skip + print("I am some_method") + return 0 + + async def some_async_method( self, unformatted, args ): # fmt: skip + print("I am some_async_method") + await asyncio.sleep(1) +if unformatted_call( args ): # fmt: skip + print("First branch") + # Make sure this is not removed. +elif another_unformatted_call( args ): # fmt: skip + print("Second branch") +else : # fmt: skip + print("Last branch") +while some_condition( unformatted, args ): # fmt: skip + print("Do something") +for i in some_iter( unformatted, args ): # fmt: skip + print("Do something") +async def test_async_for(): + async for i in some_async_iter( unformatted, args ): # fmt: skip + print("Do something") +try : # fmt: skip + some_call() +except UnformattedError as ex: # fmt: skip + handle_exception() +finally : # fmt: skip + finally_call() +with give_me_context( unformatted, args ): # fmt: skip + print("Do something") +async def test_async_with(): + async with give_me_async_context( unformatted, args ): # fmt: skip + print("Do something") +``` + +## Black Output + +```py +# Make sure a leading comment is not removed. +def some_func( unformatted, args ): # fmt: skip + print("I am some_func") + return 0 + # Make sure this comment is not removed. + + +# Make sure a leading comment is not removed. +async def some_async_func( unformatted, args): # fmt: skip + print("I am some_async_func") + await asyncio.sleep(1) + + +# Make sure a leading comment is not removed. +class SomeClass( Unformatted, SuperClasses ): # fmt: skip + def some_method( self, unformatted, args ): # fmt: skip + print("I am some_method") + return 0 + + async def some_async_method( self, unformatted, args ): # fmt: skip + print("I am some_async_method") + await asyncio.sleep(1) + + +# Make sure a leading comment is not removed. +if unformatted_call( args ): # fmt: skip + print("First branch") + # Make sure this is not removed. +elif another_unformatted_call( args ): # fmt: skip + print("Second branch") +else : # fmt: skip + print("Last branch") + + +while some_condition( unformatted, args ): # fmt: skip + print("Do something") + + +for i in some_iter( unformatted, args ): # fmt: skip + print("Do something") + + +async def test_async_for(): + async for i in some_async_iter( unformatted, args ): # fmt: skip + print("Do something") + + +try : # fmt: skip + some_call() +except UnformattedError as ex: # fmt: skip + handle_exception() +finally : # fmt: skip + finally_call() + + +with give_me_context( unformatted, args ): # fmt: skip + print("Do something") + + +async def test_async_with(): + async with give_me_async_context( unformatted, args ): # fmt: skip + print("Do something") +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__beginning_backslash_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip_py.snap similarity index 56% rename from crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__beginning_backslash_py.snap rename to crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip_py.snap index df93318969..b4cb7a2292 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__beginning_backslash_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip_py.snap @@ -1,18 +1,14 @@ --- source: crates/ruff_python_formatter/src/lib.rs expression: snapshot -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/beginning_backslash.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip.py --- ## Input ```py -\ - - - - - -print("hello, world") +a, b = 1, 2 +c = 6 # fmt: skip +d = 5 ``` ## Black Differences @@ -20,32 +16,27 @@ print("hello, world") ```diff --- Black +++ Ruff -@@ -1 +1,7 @@ -+\ -+ -+ -+ -+ -+ - print("hello, world") +@@ -1,3 +1,3 @@ + a, b = 1, 2 +-c = 6 # fmt: skip ++c = 6 + d = 5 ``` ## Ruff Output ```py -\ - - - - - -print("hello, world") +a, b = 1, 2 +c = 6 +d = 5 ``` ## Black Output ```py -print("hello, world") +a, b = 1, 2 +c = 6 # fmt: skip +d = 5 ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function2_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function2_py.snap index dfd644f3c7..6af0b38a4e 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function2_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function2_py.snap @@ -82,7 +82,7 @@ with hmm_but_this_should_get_two_preceding_newlines(): ) limited.append(-limited.pop()) # negate top return A( -@@ -13,34 +13,24 @@ +@@ -13,34 +13,22 @@ very_long_argument_name2=-very.long.value.for_the_argument, **kwargs, ) @@ -102,8 +102,8 @@ with hmm_but_this_should_get_two_preceding_newlines(): pass - print("Inner defs should breathe a little.") - - +- +- if os.name == "posix": import termios - @@ -117,7 +117,7 @@ with hmm_but_this_should_get_two_preceding_newlines(): def i_should_be_followed_by_only_one_newline(): pass -@@ -54,12 +44,10 @@ +@@ -54,12 +42,9 @@ class IHopeYouAreHavingALovelyDay: def __call__(self): print("i_should_be_followed_by_only_one_newline") @@ -127,7 +127,7 @@ with hmm_but_this_should_get_two_preceding_newlines(): def foo(): pass - - +- with hmm_but_this_should_get_two_preceding_newlines(): pass ``` @@ -159,8 +159,6 @@ def h(): def inner(): pass print("Inner defs should breathe a little.") - - if os.name == "posix": import termios def i_should_be_followed_by_only_one_newline(): @@ -185,7 +183,6 @@ else: def foo(): pass - with hmm_but_this_should_get_two_preceding_newlines(): pass ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap index d2f7810be3..6cc366ed98 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap @@ -108,10 +108,13 @@ def __await__(): return (yield) ```diff --- Black +++ Ruff -@@ -4,97 +4,52 @@ - +@@ -1,100 +1,51 @@ +-#!/usr/bin/env python3 + import asyncio + import sys +- from third_party import X, Y, Z - +- -from library import some_connection, some_decorator - -f"trigger 3.6 mode" @@ -202,7 +205,6 @@ def __await__(): return (yield) +def spaces_types(a: int = 1, b: tuple = (), c: list = [], d: dict = {}, e: bool = True, f: int = -1, g: int = 1 if False else 2, h: str = "", i: str = r''): ... +def spaces2(result= _core.Value(None)): + assert fut is self._read_fut, (fut, self._read_fut) -+ def example(session): - result = ( - session.query(models.Customer.id) @@ -242,7 +244,7 @@ def __await__(): return (yield) # trailing standalone comment ) ) -@@ -117,23 +72,18 @@ +@@ -117,23 +68,18 @@ \n? ) $ @@ -274,7 +276,7 @@ def __await__(): return (yield) ) -> A: return ( yield from A( -@@ -142,7 +92,4 @@ +@@ -142,7 +88,4 @@ **kwargs, ) ) @@ -288,12 +290,9 @@ def __await__(): return (yield) ## Ruff Output ```py -#!/usr/bin/env python3 import asyncio import sys - from third_party import X, Y, Z - from library import some_connection, \ some_decorator f'trigger 3.6 mode' @@ -324,7 +323,6 @@ def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r'' def spaces_types(a: int = 1, b: tuple = (), c: list = [], d: dict = {}, e: bool = True, f: int = -1, g: int = 1 if False else 2, h: str = "", i: str = r''): ... def spaces2(result= _core.Value(None)): assert fut is self._read_fut, (fut, self._read_fut) - def example(session): result = session.query(models.Customer.id).filter( models.Customer.account_id == account_id, diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_trailing_comma_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_trailing_comma_py.snap index 632989be77..eb3306e510 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_trailing_comma_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_trailing_comma_py.snap @@ -74,7 +74,7 @@ some_module.some_function( ```diff --- Black +++ Ruff -@@ -1,69 +1,26 @@ +@@ -1,114 +1,42 @@ -def f( - a, -): @@ -84,7 +84,7 @@ some_module.some_function( +def f(a,): + d = {'key': 'value',} tup = (1,) - +- - -def f2( - a, @@ -99,10 +99,7 @@ some_module.some_function( - 2, - ) - -+def f2(a,b,): -+ d = {'key': 'value', 'key2': 'value2',} -+ tup = (1,2,) - +- -def f( - a: int = 1, -): @@ -114,6 +111,9 @@ some_module.some_function( - call2( - arg=[1, 2, 3], - ) ++def f2(a,b,): ++ d = {'key': 'value', 'key2': 'value2',} ++ tup = (1,2,) +def f(a:int=1,): + call(arg={'explode': 'this',}) + call2(arg=[1,2,3],) @@ -136,12 +136,8 @@ some_module.some_function( - ): + if a == {"a": 1,"b": 2,"c": 3,"d": 4,"e": 5,"f": 6,"g": 7,"h": 8,}["a"]: pass - -+def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ -+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -+]: -+ json = {"k": {"k2": {"k3": [1,]}}} - +- +- -def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( - Set["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] -): @@ -154,13 +150,20 @@ some_module.some_function( - } - } - } - - - # The type annotation shouldn't get a trailing comma since that would change its type. -@@ -74,24 +31,21 @@ +- +- +-# The type annotation shouldn't get a trailing comma since that would change its type. +-# Relevant bug report: https://github.com/psf/black/issues/2381. ++def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ ++ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ++]: ++ json = {"k": {"k2": {"k3": [1,]}}} + def some_function_with_a_really_long_name() -> ( + returning_a_deeply_nested_import_of_a_type_i_suppose + ): pass - - +- +- -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: @@ -168,8 +171,8 @@ some_module.some_function( + another_case_of_returning_a_deeply_nested_import_of_a_type_i_suppose_cause_why_not +): pass - - +- +- def func() -> ( - also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( - this_shouldn_t_get_a_trailing_comma_too @@ -177,8 +180,8 @@ some_module.some_function( + also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black(this_shouldn_t_get_a_trailing_comma_too) ): pass - - +- +- -def func() -> ( - also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( +def func() -> ((also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( @@ -187,10 +190,14 @@ some_module.some_function( + )) ): pass - -@@ -103,12 +57,5 @@ - - # Inner trailing comma causes outer to explode +- +- +-# Make sure inner one-element tuple won't explode + some_module.some_function( + argument1, (one_element_tuple,), argument4, argument5, argument6 + ) +- +-# Inner trailing comma causes outer to explode some_module.some_function( - argument1, - ( @@ -210,11 +217,9 @@ some_module.some_function( def f(a,): d = {'key': 'value',} tup = (1,) - def f2(a,b,): d = {'key': 'value', 'key2': 'value2',} tup = (1,2,) - def f(a:int=1,): call(arg={'explode': 'this',}) call2(arg=[1,2,3],) @@ -224,47 +229,30 @@ def f(a:int=1,): }["a"] if a == {"a": 1,"b": 2,"c": 3,"d": 4,"e": 5,"f": 6,"g": 7,"h": 8,}["a"]: pass - def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ]: json = {"k": {"k2": {"k3": [1,]}}} - - - -# The type annotation shouldn't get a trailing comma since that would change its type. -# Relevant bug report: https://github.com/psf/black/issues/2381. def some_function_with_a_really_long_name() -> ( returning_a_deeply_nested_import_of_a_type_i_suppose ): pass - - 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 ): pass - - def func() -> ( also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black(this_shouldn_t_get_a_trailing_comma_too) ): pass - - def func() -> ((also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( this_shouldn_t_get_a_trailing_comma_too )) ): pass - - -# Make sure inner one-element tuple won't explode some_module.some_function( argument1, (one_element_tuple,), argument4, argument5, argument6 ) - -# Inner trailing comma causes outer to explode some_module.some_function( argument1, (one, two,), argument4, argument5, argument6 ) diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__import_spacing_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__import_spacing_py.snap index 13eab2b6d7..e79fdb11b4 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__import_spacing_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__import_spacing_py.snap @@ -62,10 +62,11 @@ __all__ = ( ```diff --- Black +++ Ruff -@@ -2,8 +2,10 @@ - - # flake8: noqa - +@@ -1,55 +1,30 @@ + """The asyncio package, tracking PEP 3156.""" +- +-# flake8: noqa +- -from logging import WARNING from logging import ( + WARNING @@ -74,9 +75,23 @@ __all__ = ( ERROR, ) import sys -@@ -22,33 +24,16 @@ +- +-# This relies on each of the submodules having an __all__ variable. + from .base_events import * + from .coroutines import * +-from .events import * # comment here +- ++from .events import * + from .futures import * +-from .locks import * # comment here ++from .locks import * + from .protocols import * +- +-from ..runners import * # comment here ++from ..runners import * + from ..queues import * from ..streams import * - +- from some_library import ( - Just, - Enough, @@ -99,7 +114,7 @@ __all__ = ( -) +from name_of_a_company.extremely_long_project_name.component.ttypes import CuteLittleServiceHandlerFactoryyy from name_of_a_company.extremely_long_project_name.extremely_long_component_name.ttypes import * - +- from .a.b.c.subprocess import * -from . import tasks -from . import A, B, C @@ -107,22 +122,20 @@ __all__ = ( - SomeVeryLongNameAndAllOfItsAdditionalLetters1, - SomeVeryLongNameAndAllOfItsAdditionalLetters2, -) +- +from . import (tasks) +from . import (A, B, C) +from . import SomeVeryLongNameAndAllOfItsAdditionalLetters1, \ + SomeVeryLongNameAndAllOfItsAdditionalLetters2 - __all__ = ( base_events.__all__ + + coroutines.__all__ ``` ## Ruff Output ```py """The asyncio package, tracking PEP 3156.""" - -# flake8: noqa - from logging import ( WARNING ) @@ -130,32 +143,25 @@ from logging import ( ERROR, ) import sys - -# This relies on each of the submodules having an __all__ variable. from .base_events import * from .coroutines import * -from .events import * # comment here - +from .events import * from .futures import * -from .locks import * # comment here +from .locks import * from .protocols import * - -from ..runners import * # comment here +from ..runners import * from ..queues import * from ..streams import * - from some_library import ( Just, Enough, Libraries, To, Fit, In, This, Nice, Split, Which, We, No, Longer, Use ) from name_of_a_company.extremely_long_project_name.component.ttypes import CuteLittleServiceHandlerFactoryyy from name_of_a_company.extremely_long_project_name.extremely_long_component_name.ttypes import * - from .a.b.c.subprocess import * from . import (tasks) from . import (A, B, C) from . import SomeVeryLongNameAndAllOfItsAdditionalLetters1, \ SomeVeryLongNameAndAllOfItsAdditionalLetters2 - __all__ = ( base_events.__all__ + coroutines.__all__ diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__one_element_subscript_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__one_element_subscript_py.snap index 8b1a22c94f..083c187251 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__one_element_subscript_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__one_element_subscript_py.snap @@ -25,10 +25,13 @@ list_of_types = [tuple[int,],] ```diff --- Black +++ Ruff -@@ -4,19 +4,9 @@ +@@ -1,22 +1,6 @@ +-# We should not treat the trailing comma +-# in a single-element subscript. + a: tuple[int,] b = tuple[int,] - - # The magic comma still applies to multi-element subscripts. +- +-# The magic comma still applies to multi-element subscripts. -c: tuple[ - int, - int, @@ -37,16 +40,16 @@ list_of_types = [tuple[int,],] - int, - int, -] -+c: tuple[int, int,] -+d = tuple[int, int,] - - # Magic commas still work as expected for non-subscripts. +- +-# Magic commas still work as expected for non-subscripts. -small_list = [ - 1, -] -list_of_types = [ - tuple[int,], -] ++c: tuple[int, int,] ++d = tuple[int, int,] +small_list = [1,] +list_of_types = [tuple[int,],] ``` @@ -54,16 +57,10 @@ list_of_types = [tuple[int,],] ## Ruff Output ```py -# We should not treat the trailing comma -# in a single-element subscript. a: tuple[int,] b = tuple[int,] - -# The magic comma still applies to multi-element subscripts. c: tuple[int, int,] d = tuple[int, int,] - -# Magic commas still work as expected for non-subscripts. small_list = [1,] list_of_types = [tuple[int,],] ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__power_op_spacing_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__power_op_spacing_py.snap index cc31431bdb..9001c97e7a 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__power_op_spacing_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__power_op_spacing_py.snap @@ -76,19 +76,51 @@ return np.divide( ```diff --- Black +++ Ruff -@@ -1,10 +1,10 @@ +@@ -1,16 +1,10 @@ def function(**kwargs): t = a**2 + b**3 - return t**2 +- +- + return t ** 2 - - def function_replace_spaces(**kwargs): - t = a**2 + b**3 + c**4 +- +- + t = a **2 + b** 3 + c ** 4 - - def function_dont_replace_spaces(): + {**a, **b, **c} +- +- + a = 5**~4 + b = 5 ** f() + c = -(5**2) +@@ -29,7 +23,6 @@ + p = {(k, k**2): v**2 for k, v in pairs} + q = [10**i for i in range(6)] + r = x**y +- + a = 5.0**~4.0 + b = 5.0 ** f() + c = -(5.0**2.0) +@@ -47,9 +40,6 @@ + o = settings(max_examples=10**6.0) + p = {(k, k**2): v**2.0 for k, v in pairs} + q = [10.5**i for i in range(6)] +- +- +-# WE SHOULD DEFINITELY NOT EAT THESE COMMENTS (https://github.com/psf/black/issues/2873) + if hasattr(view, "sum_of_weights"): + return np.divide( # type: ignore[no-any-return] + view.variance, # type: ignore[union-attr] +@@ -57,7 +47,6 @@ + out=np.full(view.sum_of_weights.shape, np.nan), # type: ignore[union-attr] + where=view.sum_of_weights**2 > view.sum_of_weights_squared, # type: ignore[union-attr] + ) +- + return np.divide( + where=view.sum_of_weights_of_weight_long**2 > view.sum_of_weights_squared, # type: ignore + ) ``` ## Ruff Output @@ -97,16 +129,10 @@ return np.divide( def function(**kwargs): t = a**2 + b**3 return t ** 2 - - def function_replace_spaces(**kwargs): t = a **2 + b** 3 + c ** 4 - - def function_dont_replace_spaces(): {**a, **b, **c} - - a = 5**~4 b = 5 ** f() c = -(5**2) @@ -125,7 +151,6 @@ o = settings(max_examples=10**6) p = {(k, k**2): v**2 for k, v in pairs} q = [10**i for i in range(6)] r = x**y - a = 5.0**~4.0 b = 5.0 ** f() c = -(5.0**2.0) @@ -143,9 +168,6 @@ n = count <= 10**5.0 o = settings(max_examples=10**6.0) p = {(k, k**2): v**2.0 for k, v in pairs} q = [10.5**i for i in range(6)] - - -# WE SHOULD DEFINITELY NOT EAT THESE COMMENTS (https://github.com/psf/black/issues/2873) if hasattr(view, "sum_of_weights"): return np.divide( # type: ignore[no-any-return] view.variance, # type: ignore[union-attr] @@ -153,7 +175,6 @@ if hasattr(view, "sum_of_weights"): out=np.full(view.sum_of_weights.shape, np.nan), # type: ignore[union-attr] where=view.sum_of_weights**2 > view.sum_of_weights_squared, # type: ignore[union-attr] ) - return np.divide( where=view.sum_of_weights_of_weight_long**2 > view.sum_of_weights_squared, # type: ignore ) diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__prefer_rhs_split_reformatted_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__prefer_rhs_split_reformatted_py.snap index 9f60197f9f..b0279964fa 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__prefer_rhs_split_reformatted_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__prefer_rhs_split_reformatted_py.snap @@ -25,10 +25,11 @@ xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxx ```diff --- Black +++ Ruff -@@ -2,20 +2,11 @@ - - # Left hand side fits in a single line but will still be exploded by the - # magic trailing comma. +@@ -1,21 +1,5 @@ +-# Test cases separate from `prefer_rhs_split.py` that contains unformatted source. +- +-# Left hand side fits in a single line but will still be exploded by the +-# magic trailing comma. -( - first_value, - ( @@ -41,9 +42,9 @@ xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxx arg1, arg2, ) - - # Make when when the left side of assignment plus the opening paren "... = (" is - # exactly line length limit + 1, it won't be split like that. +- +-# Make when when the left side of assignment plus the opening paren "... = (" is +-# exactly line length limit + 1, it won't be split like that. -xxxxxxxxx_yyy_zzzzzzzz[ - xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) -] = 1 @@ -53,17 +54,10 @@ xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxx ## Ruff Output ```py -# Test cases separate from `prefer_rhs_split.py` that contains unformatted source. - -# Left hand side fits in a single line but will still be exploded by the -# magic trailing comma. first_value, (m1, m2,), third_value = xxxxxx_yyyyyy_zzzzzz_wwwwww_uuuuuuu_vvvvvvvvvvv( arg1, arg2, ) - -# Make when when the left side of assignment plus the opening paren "... = (" is -# exactly line length limit + 1, it won't be split like that. xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1)] = 1 ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_await_parens_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_await_parens_py.snap index be33aaae33..c8c9c2034b 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_await_parens_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_await_parens_py.snap @@ -94,88 +94,90 @@ async def main(): ```diff --- Black +++ Ruff -@@ -1,93 +1,81 @@ +@@ -1,93 +1,56 @@ import asyncio - - - # Control example +- +-# Control example async def main(): await asyncio.sleep(1) - - - # Remove brackets for short coroutine/task +- +-# Remove brackets for short coroutine/task async def main(): - await asyncio.sleep(1) - +- + await (asyncio.sleep(1)) - async def main(): - await asyncio.sleep(1) - +- + await ( + asyncio.sleep(1) + ) - async def main(): - await asyncio.sleep(1) - +- +-# Check comments +-async def main(): +- await asyncio.sleep(1) # Hello +- +- +-async def main(): +- await asyncio.sleep(1) # Hello +- +- + await (asyncio.sleep(1) + ) - - # Check comments async def main(): - await asyncio.sleep(1) # Hello - +- +-# Long lines + await ( # Hello + asyncio.sleep(1) + ) - async def main(): -- await asyncio.sleep(1) # Hello -- +- await asyncio.gather( +- asyncio.sleep(1), +- asyncio.sleep(1), +- asyncio.sleep(1), +- asyncio.sleep(1), +- asyncio.sleep(1), +- asyncio.sleep(1), +- asyncio.sleep(1), + await ( + asyncio.sleep(1) # Hello -+ ) - - async def main(): -- await asyncio.sleep(1) # Hello + ) - +- +-# Same as above but with magic trailing comma in function + async def main(): +- await asyncio.gather( +- asyncio.sleep(1), +- asyncio.sleep(1), +- asyncio.sleep(1), +- asyncio.sleep(1), +- asyncio.sleep(1), +- asyncio.sleep(1), +- asyncio.sleep(1), + await ( + asyncio.sleep(1) -+ ) # Hello - - # Long lines - async def main(): -- await asyncio.gather( -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- ) + ) - -+ await asyncio.gather(asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1)) - - # Same as above but with magic trailing comma in function - async def main(): -- await asyncio.gather( -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- asyncio.sleep(1), -- ) - -+ await asyncio.gather(asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1),) - - # Cr@zY Br@ck3Tz +-# Cr@zY Br@ck3Tz async def main(): - await black(1) - +- +-# Keep brackets around non power operations and nested awaits ++ await asyncio.gather(asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1)) ++async def main(): ++ await asyncio.gather(asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1),) ++async def main(): + await ( + ((((((((((((( + ((( ((( @@ -189,35 +191,33 @@ async def main(): + ))) ))) + ))))))))))))) + ) - - # Keep brackets around non power operations and nested awaits async def main(): await (set_of_tasks | other_set) - - +- async def main(): await (await asyncio.sleep(1)) - - - # It's awaits all the way down... +- +-# It's awaits all the way down... async def main(): await (await x) - - +- async def main(): await (yield x) - - +- async def main(): - await (await asyncio.sleep(1)) - +- + await (await (asyncio.sleep(1))) - async def main(): - await (await (await (await (await asyncio.sleep(1))))) - +- + await (await (await (await (await (asyncio.sleep(1)))))) - async def main(): await (yield) ``` @@ -226,49 +226,33 @@ async def main(): ```py import asyncio - -# Control example async def main(): await asyncio.sleep(1) - -# Remove brackets for short coroutine/task async def main(): await (asyncio.sleep(1)) - async def main(): await ( asyncio.sleep(1) ) - async def main(): await (asyncio.sleep(1) ) - -# Check comments async def main(): await ( # Hello asyncio.sleep(1) ) - async def main(): await ( asyncio.sleep(1) # Hello ) - async def main(): await ( asyncio.sleep(1) - ) # Hello - -# Long lines + ) async def main(): await asyncio.gather(asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1)) - -# Same as above but with magic trailing comma in function async def main(): await asyncio.gather(asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1),) - -# Cr@zY Br@ck3Tz async def main(): await ( ((((((((((((( @@ -283,27 +267,18 @@ async def main(): ))) ))) ))))))))))))) ) - -# Keep brackets around non power operations and nested awaits async def main(): await (set_of_tasks | other_set) - async def main(): await (await asyncio.sleep(1)) - -# It's awaits all the way down... async def main(): await (await x) - async def main(): await (yield x) - async def main(): await (await (asyncio.sleep(1))) - async def main(): await (await (await (await (await (asyncio.sleep(1)))))) - async def main(): await (yield) ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_except_parens_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_except_parens_py.snap index 8f02ffe3f1..973db766a1 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_except_parens_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_except_parens_py.snap @@ -48,17 +48,29 @@ except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.ov ```diff --- Black +++ Ruff -@@ -1,7 +1,7 @@ - # These brackets are redundant, therefore remove. +@@ -1,42 +1,24 @@ +-# These brackets are redundant, therefore remove. try: a.something -except AttributeError as err: +except (AttributeError) as err: raise err - - # This is tuple of exceptions. -@@ -21,22 +21,15 @@ - # Test long variants. +- +-# This is tuple of exceptions. +-# Although this could be replaced with just the exception, +-# we do not remove brackets to preserve AST. + try: + a.something + except (AttributeError,) as err: + raise err +- +-# This is a tuple of exceptions. Do not remove brackets. + try: + a.something + except (AttributeError, ValueError) as err: + raise err +- +-# Test long variants. try: a.something -except ( @@ -66,7 +78,7 @@ except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.ov -) as err: +except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error) as err: raise err - +- try: a.something -except ( @@ -74,7 +86,7 @@ except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.ov -) as err: +except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error,) as err: raise err - +- try: a.something -except ( @@ -88,37 +100,26 @@ except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.ov ## Ruff Output ```py -# These brackets are redundant, therefore remove. try: a.something except (AttributeError) as err: raise err - -# This is tuple of exceptions. -# Although this could be replaced with just the exception, -# we do not remove brackets to preserve AST. try: a.something except (AttributeError,) as err: raise err - -# This is a tuple of exceptions. Do not remove brackets. try: a.something except (AttributeError, ValueError) as err: raise err - -# Test long variants. try: a.something except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error) as err: raise err - try: a.something except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error,) as err: raise err - try: a.something except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error, some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error) as err: diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_for_brackets_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_for_brackets_py.snap index 7f0c038d1f..313beba4d2 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_for_brackets_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_for_brackets_py.snap @@ -32,24 +32,25 @@ for (((((k, v))))) in d.items(): ```diff --- Black +++ Ruff -@@ -1,5 +1,5 @@ - # Only remove tuple brackets after `for` +@@ -1,27 +1,11 @@ +-# Only remove tuple brackets after `for` -for k, v in d.items(): +for (k, v) in d.items(): print(k, v) - - # Don't touch tuple brackets after `in` -@@ -8,20 +8,12 @@ +- +-# Don't touch tuple brackets after `in` + for module in (core, _unicodefun): + if hasattr(module, "_verify_python3_env"): module._verify_python3_env = lambda: None - - # Brackets remain for long for loop lines +- +-# Brackets remain for long for loop lines -for ( - why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long, - i_dont_know_but_we_should_still_check_the_behaviour_if_they_do, -) in d.items(): +for (why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long, i_dont_know_but_we_should_still_check_the_behaviour_if_they_do) in d.items(): print(k, v) - +- -for ( - k, - v, @@ -58,8 +59,8 @@ for (((((k, v))))) in d.items(): -): +for (k, v) in dfkasdjfldsjflkdsjflkdsjfdslkfjldsjfgkjdshgkljjdsfldgkhsdofudsfudsofajdslkfjdslkfjldisfjdffjsdlkfjdlkjjkdflskadjldkfjsalkfjdasj.items(): print(k, v) - - # Test deeply nested brackets +- +-# Test deeply nested brackets -for k, v in d.items(): +for (((((k, v))))) in d.items(): print(k, v) @@ -68,23 +69,15 @@ for (((((k, v))))) in d.items(): ## Ruff Output ```py -# Only remove tuple brackets after `for` for (k, v) in d.items(): print(k, v) - -# Don't touch tuple brackets after `in` for module in (core, _unicodefun): if hasattr(module, "_verify_python3_env"): module._verify_python3_env = lambda: None - -# Brackets remain for long for loop lines for (why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long, i_dont_know_but_we_should_still_check_the_behaviour_if_they_do) in d.items(): print(k, v) - for (k, v) in dfkasdjfldsjflkdsjflkdsjfdslkfjldsjfgkjdshgkljjdsfldgkhsdofudsfudsofajdslkfjdslkfjldisfjdffjsdlkfjdlkjjkdflskadjldkfjsalkfjdasj.items(): print(k, v) - -# Test deeply nested brackets for (((((k, v))))) in d.items(): print(k, v) ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_newline_after_code_block_open_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_newline_after_code_block_open_py.snap index f632858d07..bb3d5adbec 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_newline_after_code_block_open_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_newline_after_code_block_open_py.snap @@ -121,107 +121,104 @@ with open("/path/to/file.txt", mode="r") as read_file: ```diff --- Black +++ Ruff -@@ -2,20 +2,26 @@ - - +@@ -1,78 +1,74 @@ + import random +- +- def foo1(): -+ - print("The newline above me should be deleted!") - +- print("The newline above me should be deleted!") +- ++ print("The newline above me should be deleted!") def foo2(): -+ -+ -+ - print("All the newlines above me should be deleted!") +- print("All the newlines above me should be deleted!") ++ ++ print("All the newlines above me should be deleted!") def foo3(): + print("No newline above me!") print("There is a newline above me, and that's OK!") ++def foo4(): - - def foo4(): -+ +- +-def foo4(): # There is a comment here print("The newline above me should not be deleted!") -@@ -23,27 +29,39 @@ - +- +- class Foo: def bar(self): + print("The newline above me should be deleted!") - - +- +- for i in range(5): -+ - print(f"{i}) The line above me should be removed!") - +- print(f"{i}) The line above me should be removed!") +- ++ print(f"{i}) The line above me should be removed!") for i in range(5): -+ -+ -+ - print(f"{i}) The lines above me should be removed!") +- print(f"{i}) The lines above me should be removed!") ++ ++ print(f"{i}) The lines above me should be removed!") for i in range(5): + for j in range(7): + print(f"{i}) The lines above me should be removed!") ++if random.randint(0, 3) == 0: - +- ++ print("The new line above me is about to be removed!") if random.randint(0, 3) == 0: -+ - print("The new line above me is about to be removed!") +- print("The new line above me is about to be removed!") +-if random.randint(0, 3) == 0: +- print("The new lines above me is about to be removed!") + + ++ print("The new lines above me is about to be removed!") if random.randint(0, 3) == 0: -+ -+ -+ -+ - print("The new lines above me is about to be removed!") - - -@@ -53,26 +71,38 @@ - + if random.uniform(0, 1) > 0.5: + print("Two lines above me are about to be removed!") +- ++while True: ++ print("The newline above me should be deleted!") while True: -+ - print("The newline above me should be deleted!") +- print("The newline above me should be deleted!") - while True: -+ -+ +-while True: + print("The newlines above me should be deleted!") ++while True: - - while True: -+ +- +-while True: while False: -+ - print("The newlines above me should be deleted!") - +- print("The newlines above me should be deleted!") +- ++ print("The newlines above me should be deleted!") with open("/path/to/file.txt", mode="w") as file: -+ - file.write("The new line above me is about to be removed!") - +- file.write("The new line above me is about to be removed!") +- ++ file.write("The new line above me is about to be removed!") with open("/path/to/file.txt", mode="w") as file: -+ -+ -+ - file.write("The new lines above me is about to be removed!") +- file.write("The new lines above me is about to be removed!") ++ ++ file.write("The new lines above me is about to be removed!") with open("/path/to/file.txt", mode="r") as read_file: + with open("/path/to/output_file.txt", mode="w") as write_file: @@ -233,108 +230,74 @@ with open("/path/to/file.txt", mode="r") as read_file: ```py import random - - def foo1(): print("The newline above me should be deleted!") - - def foo2(): print("All the newlines above me should be deleted!") - - def foo3(): print("No newline above me!") print("There is a newline above me, and that's OK!") - - def foo4(): # There is a comment here print("The newline above me should not be deleted!") - - class Foo: def bar(self): print("The newline above me should be deleted!") - - for i in range(5): print(f"{i}) The line above me should be removed!") - - for i in range(5): print(f"{i}) The lines above me should be removed!") - - for i in range(5): for j in range(7): print(f"{i}) The lines above me should be removed!") - - if random.randint(0, 3) == 0: print("The new line above me is about to be removed!") - - if random.randint(0, 3) == 0: print("The new lines above me is about to be removed!") - - if random.randint(0, 3) == 0: if random.uniform(0, 1) > 0.5: print("Two lines above me are about to be removed!") - - while True: print("The newline above me should be deleted!") - - while True: print("The newlines above me should be deleted!") - - while True: while False: print("The newlines above me should be deleted!") - - with open("/path/to/file.txt", mode="w") as file: file.write("The new line above me is about to be removed!") - - with open("/path/to/file.txt", mode="w") as file: file.write("The new lines above me is about to be removed!") - - with open("/path/to/file.txt", mode="r") as read_file: with open("/path/to/output_file.txt", mode="w") as write_file: diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_parens_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_parens_py.snap index bb41990dda..d06c27e3a0 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_parens_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_parens_py.snap @@ -68,16 +68,16 @@ def example8(): ```diff --- Black +++ Ruff -@@ -1,85 +1,55 @@ +@@ -1,85 +1,34 @@ -x = 1 -x = 1.2 +- +x = (1) +x = (1.2) - data = ( "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ).encode() - +- - async def show_status(): while True: @@ -91,51 +91,51 @@ def example8(): except Exception as e: pass - - +- def example(): - return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +- +- + return (("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")) - - def example1(): - return 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +- +- + return ((1111111111111111111111111111111111111111111111111111111111111111111111111111111111111)) - - def example1point5(): - return 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +- +- + return ((((((1111111111111111111111111111111111111111111111111111111111111111111111111111111111111)))))) - - def example2(): - return ( - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - ) +- +- + return (("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")) - - def example3(): - return ( - 1111111111111111111111111111111111111111111111111111111111111111111111111111111 - ) +- +- + return ((1111111111111111111111111111111111111111111111111111111111111111111111111111111)) - - def example4(): - return True +- +- + return ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((True)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) - - def example5(): - return () +- +- + return ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) - - def example6(): - return {a: a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]} +- +- + return ((((((((({a:a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]}))))))))) - - def example7(): - return { - a: a @@ -162,9 +162,9 @@ def example8(): - 20000000000000000000, - ] - } +- +- + return ((((((((({a:a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20000000000000000000]}))))))))) - - def example8(): - return None + return (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((None))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) @@ -175,11 +175,9 @@ def example8(): ```py x = (1) x = (1.2) - data = ( "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ).encode() - async def show_status(): while True: try: @@ -189,43 +187,24 @@ async def show_status(): ).encode() except Exception as e: pass - def example(): return (("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")) - - def example1(): return ((1111111111111111111111111111111111111111111111111111111111111111111111111111111111111)) - - def example1point5(): return ((((((1111111111111111111111111111111111111111111111111111111111111111111111111111111111111)))))) - - def example2(): return (("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")) - - def example3(): return ((1111111111111111111111111111111111111111111111111111111111111111111111111111111)) - - def example4(): return ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((True)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) - - def example5(): return ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) - - def example6(): return ((((((((({a:a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]}))))))))) - - def example7(): return ((((((((({a:a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20000000000000000000]}))))))))) - - def example8(): return (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((None))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__return_annotation_brackets_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__return_annotation_brackets_py.snap index c4f4e99033..14ddbe8bf6 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__return_annotation_brackets_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__return_annotation_brackets_py.snap @@ -101,81 +101,81 @@ def foo() -> tuple[int, int, int,]: ```diff --- Black +++ Ruff -@@ -1,120 +1,88 @@ - # Control +@@ -1,120 +1,60 @@ +-# Control +-def double(a: int) -> int: +- return 2 * a +- +- +-# Remove the brackets +-def double(a: int) -> int: +- return 2 * a +- +- +-# Some newline variations +-def double(a: int) -> int: +- return 2 * a +- +- +-def double(a: int) -> int: +- return 2 * a +- +- def double(a: int) -> int: - return 2 * a -+ return 2*a - - - # Remove the brackets --def double(a: int) -> int: +- +-# Don't lose the comments +-def double(a: int) -> int: # Hello - return 2 * a - +- +-def double(a: int) -> int: # Hello +- return 2 * a +- +- +-# Really long annotations +-def foo() -> ( +- intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds ++ return 2*a +def double(a: int) -> (int): + return 2*a - - # Some newline variations --def double(a: int) -> int: -- return 2 * a +def double(a: int) -> ( + int): + return 2*a - -- --def double(a: int) -> int: -- return 2 * a -- -- --def double(a: int) -> int: -- return 2 * a +def double(a: int) -> (int +): + return 2*a - +def double(a: int) -> ( + int +): + return 2*a - - # Don't lose the comments --def double(a: int) -> int: # Hello -- return 2 * a -- +def double(a: int) -> ( # Hello + int -+): -+ return 2*a - --def double(a: int) -> int: # Hello -- return 2 * a + ): +- return 2 - +- ++ return 2*a +def double(a: int) -> ( + int # Hello +): + return 2*a - - # Really long annotations def foo() -> ( intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds ): return 2 - - --def foo() -> ( -- intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds --): -+def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: - return 2 - - -def foo() -> ( - intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds - | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds -): ++def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: ++ return 2 +def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: return 2 - +- - -def foo( - a: int, @@ -184,7 +184,7 @@ def foo() -> tuple[int, int, int,]: -) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: +def foo(a: int, b: int, c: int,) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: return 2 - +- - -def foo( - a: int, @@ -196,9 +196,9 @@ def foo() -> tuple[int, int, int,]: -): +def foo(a: int, b: int, c: int,) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: return 2 - - - # Split args but no need to split return +- +-# Split args but no need to split return -def foo( - a: int, - b: int, @@ -206,19 +206,20 @@ def foo() -> tuple[int, int, int,]: -) -> int: +def foo(a: int, b: int, c: int,) -> int: return 2 - - - # Deeply nested brackets - # with *interesting* spacing +- +-# Deeply nested brackets +-# with *interesting* spacing -def double(a: int) -> int: - return 2 * a - - -def double(a: int) -> int: - return 2 * a +- +- +def double(a: int) -> (((((int))))): + return 2*a - +def double(a: int) -> ( + ( ( + ((int) @@ -227,7 +228,6 @@ def foo() -> tuple[int, int, int,]: + ) + ): + return 2*a - def foo() -> ( + ( ( intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds @@ -235,9 +235,9 @@ def foo() -> tuple[int, int, int,]: +) +)): return 2 - - - # Return type with commas +- +-# Return type with commas -def foo() -> tuple[int, int, int]: - return 2 - @@ -251,11 +251,9 @@ def foo() -> tuple[int, int, int,]: + tuple[int, int, int] ): return 2 - -+def foo() -> tuple[loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong]: -+ return 2 - - # Magic trailing comma example +- +- +-# Magic trailing comma example -def foo() -> ( - tuple[ - int, @@ -263,6 +261,8 @@ def foo() -> tuple[int, int, int,]: - int, - ] -): ++def foo() -> tuple[loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong]: ++ return 2 +def foo() -> tuple[int, int, int,]: return 2 ``` @@ -270,66 +270,44 @@ def foo() -> tuple[int, int, int,]: ## Ruff Output ```py -# Control def double(a: int) -> int: return 2*a - -# Remove the brackets def double(a: int) -> (int): return 2*a - -# Some newline variations def double(a: int) -> ( int): return 2*a - def double(a: int) -> (int ): return 2*a - def double(a: int) -> ( int ): return 2*a - -# Don't lose the comments def double(a: int) -> ( # Hello int ): return 2*a - def double(a: int) -> ( int # Hello ): return 2*a - -# Really long annotations def foo() -> ( intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds ): return 2 - def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: return 2 - def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: return 2 - def foo(a: int, b: int, c: int,) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: return 2 - def foo(a: int, b: int, c: int,) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: return 2 - -# Split args but no need to split return def foo(a: int, b: int, c: int,) -> int: return 2 - -# Deeply nested brackets -# with *interesting* spacing def double(a: int) -> (((((int))))): return 2*a - def double(a: int) -> ( ( ( ((int) @@ -338,24 +316,18 @@ def double(a: int) -> ( ) ): return 2*a - def foo() -> ( ( ( intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds ) )): return 2 - -# Return type with commas def foo() -> ( tuple[int, int, int] ): return 2 - def foo() -> tuple[loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong]: return 2 - -# Magic trailing comma example def foo() -> tuple[int, int, int,]: return 2 ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__skip_magic_trailing_comma_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__skip_magic_trailing_comma_py.snap index 4b710c3dde..b2dbb81ba6 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__skip_magic_trailing_comma_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__skip_magic_trailing_comma_py.snap @@ -60,30 +60,38 @@ func( ```diff --- Black +++ Ruff -@@ -3,23 +3,45 @@ +@@ -1,25 +1,35 @@ +-# We should not remove the trailing comma in a single-element subscript. + a: tuple[int,] b = tuple[int,] - - # But commas in multiple element subscripts should be removed. +- +-# But commas in multiple element subscripts should be removed. -c: tuple[int, int] -d = tuple[int, int] -+c: tuple[int, int,] -+d = tuple[int, int,] - - # Remove commas for non-subscripts. +- +-# Remove commas for non-subscripts. -small_list = [1] -list_of_types = [tuple[int,]] -small_set = {1} -set_of_types = {tuple[int,]} +- +-# Except single element tuples ++c: tuple[int, int,] ++d = tuple[int, int,] +small_list = [1,] +list_of_types = [tuple[int,],] +small_set = {1,} +set_of_types = {tuple[int,],} - - # Except single element tuples small_tuple = (1,) - - # Trailing commas in multiple chained non-nested parens. +- +-# Trailing commas in multiple chained non-nested parens. -zero(one).two(three).four(five) +- +-func1(arg1).func2(arg2).func3(arg3).func4(arg4).func5(arg5) +- +-(a, b, c, d) = func1(arg1) and func2(arg2) +- +-func(argument1, (one, two), argument4, argument5, argument6) +zero( + one, +).two( @@ -91,11 +99,7 @@ func( +).four( + five, +) - --func1(arg1).func2(arg2).func3(arg3).func4(arg4).func5(arg5) +func1(arg1).func2(arg2,).func3(arg3).func4(arg4,).func5(arg5) - --(a, b, c, d) = func1(arg1) and func2(arg2) +( + a, + b, @@ -104,8 +108,6 @@ func( +) = func1( + arg1 +) and func2(arg2) - --func(argument1, (one, two), argument4, argument5, argument6) +func( + argument1, + ( @@ -121,24 +123,15 @@ func( ## Ruff Output ```py -# We should not remove the trailing comma in a single-element subscript. a: tuple[int,] b = tuple[int,] - -# But commas in multiple element subscripts should be removed. c: tuple[int, int,] d = tuple[int, int,] - -# Remove commas for non-subscripts. small_list = [1,] list_of_types = [tuple[int,],] small_set = {1,} set_of_types = {tuple[int,],} - -# Except single element tuples small_tuple = (1,) - -# Trailing commas in multiple chained non-nested parens. zero( one, ).two( @@ -146,9 +139,7 @@ zero( ).four( five, ) - func1(arg1).func2(arg2,).func3(arg3).func4(arg4,).func5(arg5) - ( a, b, @@ -157,7 +148,6 @@ func1(arg1).func2(arg2,).func3(arg3).func4(arg4,).func5(arg5) ) = func1( arg1 ) and func2(arg2) - func( argument1, ( diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__slices_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__slices_py.snap index 902e4035e2..d13a796fed 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__slices_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__slices_py.snap @@ -76,7 +76,35 @@ x[ ```diff --- Black +++ Ruff -@@ -56,4 +56,8 @@ +@@ -17,19 +17,12 @@ + slice[not so_simple : 1 < val <= 10] + slice[(1 for i in range(42)) : x] + slice[:: [i for i in range(42)]] +- +- + async def f(): + slice[await x : [i async for i in arange(42)] : 42] +- +- +-# These are from PEP-8: + ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] + ham[lower:upper], ham[lower:upper:], ham[lower::step] +-# ham[lower+offset : upper+offset] + ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] + ham[lower + offset : upper + offset] +- + slice[::, ::] + slice[ + # A +@@ -46,7 +39,6 @@ + # C + 3 + ] +- + slice[ + # A + 1 +@@ -56,4 +48,8 @@ # C 4 ] @@ -110,19 +138,12 @@ slice[1 or 2 : True and False] slice[not so_simple : 1 < val <= 10] slice[(1 for i in range(42)) : x] slice[:: [i for i in range(42)]] - - async def f(): slice[await x : [i async for i in arange(42)] : 42] - - -# These are from PEP-8: ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] ham[lower:upper], ham[lower:upper:], ham[lower::step] -# ham[lower+offset : upper+offset] ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] ham[lower + offset : upper + offset] - slice[::, ::] slice[ # A @@ -139,7 +160,6 @@ slice[ # C 3 ] - slice[ # A 1 diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__string_prefixes_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__string_prefixes_py.snap index 07e263b90e..71cd82d86f 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__string_prefixes_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__string_prefixes_py.snap @@ -33,9 +33,9 @@ def docstring_multiline(): ```diff --- Black +++ Ruff -@@ -1,13 +1,13 @@ - #!/usr/bin/env python3 - +@@ -1,19 +1,12 @@ +-#!/usr/bin/env python3 +- name = "Łukasz" -(f"hello {name}", f"hello {name}") -(b"", b"") @@ -44,35 +44,34 @@ def docstring_multiline(): +(b"", B"") +(u"", U"") (r"", R"") - +- -(rf"", rf"", Rf"", Rf"", rf"", rf"", Rf"", Rf"") -(rb"", rb"", Rb"", Rb"", rb"", rb"", Rb"", Rb"") +- +- +(rf"", fr"", Rf"", fR"", rF"", Fr"", RF"", FR"") +(rb"", br"", Rb"", bR"", rB"", Br"", RB"", BR"") - - def docstring_singleline(): + R"""2020 was one hell of a year. The good news is that we were able to""" +- +- + def docstring_multiline(): + R""" + clear out all of the issues opened in that time :p ``` ## Ruff Output ```py -#!/usr/bin/env python3 - name = "Łukasz" (f"hello {name}", F"hello {name}") (b"", B"") (u"", U"") (r"", R"") - (rf"", fr"", Rf"", fR"", rF"", Fr"", RF"", FR"") (rb"", br"", Rb"", bR"", rB"", Br"", RB"", BR"") - - def docstring_singleline(): R"""2020 was one hell of a year. The good news is that we were able to""" - - def docstring_multiline(): R""" clear out all of the issues opened in that time :p diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__torture_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__torture_py.snap index 8c44804b0d..ab2001454a 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__torture_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__torture_py.snap @@ -42,15 +42,15 @@ assert ( ```diff --- Black +++ Ruff -@@ -1,58 +1,29 @@ --importA +@@ -1,58 +1,25 @@ + importA -( - () - << 0 - ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 -) # -+importA;() << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 # - +- ++() << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 assert sort_by_dependency( { - "1": {"2", "3"}, @@ -64,13 +64,13 @@ assert ( + "2a": set(), "2b": set(), "3a": set(), "3b": set() } ) == ["2a", "2b", "2", "3a", "3b", "3", "1"] - +- importA --0 + 0 -0 ^ 0 # - -+0;0^0# - +- ++0^0 class A: def foo(self): for _ in range(10): @@ -79,8 +79,8 @@ assert ( xxxxxxxxxxxx - ) # pylint: disable=no-member - +- + ) - def test(self, othr): - return 1 == 2 and ( - name, @@ -101,15 +101,15 @@ assert ( - othr.meta_data, - othr.schedule, - ) -+ return (1 == 2 and -+ (name, description, self.default, self.selected, self.auto_generated, self.parameters, self.meta_data, self.schedule) == -+ (name, description, othr.default, othr.selected, othr.auto_generated, othr.parameters, othr.meta_data, othr.schedule)) - - +- +- -assert a_function( - very_long_arguments_that_surpass_the_limit, - which_is_eighty_eight_in_this_case_plus_a_bit_more, -) == {"x": "this need to pass the line limit as well", "b": "but only by a little bit"} ++ return (1 == 2 and ++ (name, description, self.default, self.selected, self.auto_generated, self.parameters, self.meta_data, self.schedule) == ++ (name, description, othr.default, othr.selected, othr.auto_generated, othr.parameters, othr.meta_data, othr.schedule)) +assert ( + a_function(very_long_arguments_that_surpass_the_limit, which_is_eighty_eight_in_this_case_plus_a_bit_more) + == {"x": "this need to pass the line limit as well", "b": "but only by a little bit"} @@ -119,31 +119,27 @@ assert ( ## Ruff Output ```py -importA;() << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 # - +importA +() << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 assert sort_by_dependency( { "1": {"2", "3"}, "2": {"2a", "2b"}, "3": {"3a", "3b"}, "2a": set(), "2b": set(), "3a": set(), "3b": set() } ) == ["2a", "2b", "2", "3a", "3b", "3", "1"] - importA -0;0^0# - +0 +0^0 class A: def foo(self): for _ in range(10): aaaaaaaaaaaaaaaaaaa = bbbbbbbbbbbbbbb.cccccccccc( # pylint: disable=no-member xxxxxxxxxxxx ) - def test(self, othr): return (1 == 2 and (name, description, self.default, self.selected, self.auto_generated, self.parameters, self.meta_data, self.schedule) == (name, description, othr.default, othr.selected, othr.auto_generated, othr.parameters, othr.meta_data, othr.schedule)) - - assert ( a_function(very_long_arguments_that_surpass_the_limit, which_is_eighty_eight_in_this_case_plus_a_bit_more) == {"x": "this need to pass the line limit as well", "b": "but only by a little bit"} diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens1_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens1_py.snap index c3362ebff7..7fea6d8abe 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens1_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens1_py.snap @@ -38,7 +38,7 @@ class A: ```diff --- Black +++ Ruff -@@ -1,19 +1,11 @@ +@@ -1,29 +1,17 @@ -if e1234123412341234.winerror not in ( - _winapi.ERROR_SEM_TIMEOUT, - _winapi.ERROR_PIPE_BUSY, @@ -46,7 +46,7 @@ class A: +if e1234123412341234.winerror not in (_winapi.ERROR_SEM_TIMEOUT, + _winapi.ERROR_PIPE_BUSY) or _check_timeout(t): pass - +- if x: if y: - new_id = ( @@ -57,21 +57,22 @@ class A: - + 1 - ) - +- + new_id = max(Vegetable.objects.order_by('-id')[0].id, + Mineral.objects.order_by('-id')[0].id) + 1 - class X: def get_help_text(self): -@@ -21,8 +13,7 @@ + return ngettext( "Your password must contain at least %(min_length)d character.", "Your password must contain at least %(min_length)d characters.", self.min_length, - ) % {"min_length": self.min_length} - +- + ) % {'min_length': self.min_length} - class A: def b(self): + if self.connection.mysql_is_mariadb and ( ``` ## Ruff Output @@ -80,12 +81,10 @@ class A: if e1234123412341234.winerror not in (_winapi.ERROR_SEM_TIMEOUT, _winapi.ERROR_PIPE_BUSY) or _check_timeout(t): pass - if x: if y: new_id = max(Vegetable.objects.order_by('-id')[0].id, Mineral.objects.order_by('-id')[0].id) + 1 - class X: def get_help_text(self): return ngettext( @@ -93,7 +92,6 @@ class X: "Your password must contain at least %(min_length)d characters.", self.min_length, ) % {'min_length': self.min_length} - class A: def b(self): if self.connection.mysql_is_mariadb and ( diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_commas_in_leading_parts_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_commas_in_leading_parts_py.snap index 80f18068cb..6878c50302 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_commas_in_leading_parts_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_commas_in_leading_parts_py.snap @@ -46,7 +46,7 @@ assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( ```diff --- Black +++ Ruff -@@ -1,28 +1,11 @@ +@@ -1,31 +1,7 @@ -zero( - one, -).two( @@ -54,18 +54,18 @@ assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( -).four( - five, -) -+zero(one,).two(three,).four(five,) - +- -func1(arg1).func2( - arg2, -).func3(arg3).func4( - arg4, -).func5(arg5) +- +-# Inner one-element tuple shouldn't explode ++zero(one,).two(three,).four(five,) +func1(arg1).func2(arg2,).func3(arg3).func4(arg4,).func5(arg5) - - # Inner one-element tuple shouldn't explode func1(arg1).func2(arg1, (one_tuple,)).func3(arg3) - +- -( - a, - b, @@ -74,26 +74,39 @@ assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( -) = func1( - arg1 -) and func2(arg2) +- +- +-# Example from https://github.com/psf/black/issues/3229 +(a, b, c, d,) = func1(arg1) and func2(arg2) - - - # Example from https://github.com/psf/black/issues/3229 + def refresh_token(self, device_family, refresh_token, api_key): + return self.orchestration.refresh_token( + data={ +@@ -33,16 +9,10 @@ + }, + api_key=api_key, + )["extensions"]["sdk"]["token"] +- +- +-# Edge case where a bug in a working-in-progress version of +-# https://github.com/psf/black/pull/3370 causes an infinite recursion. + assert ( + long_module.long_class.long_func().another_func() + == long_module.long_class.long_func()["some_key"].another_func(arg1) + ) +- +-# Regression test for https://github.com/psf/black/issues/3414. + assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( + xxxxxxxxx + ).xxxxxxxxxxxxxxxxxx(), ( ``` ## Ruff Output ```py zero(one,).two(three,).four(five,) - func1(arg1).func2(arg2,).func3(arg3).func4(arg4,).func5(arg5) - -# Inner one-element tuple shouldn't explode func1(arg1).func2(arg1, (one_tuple,)).func3(arg3) - (a, b, c, d,) = func1(arg1) and func2(arg2) - - -# Example from https://github.com/psf/black/issues/3229 def refresh_token(self, device_family, refresh_token, api_key): return self.orchestration.refresh_token( data={ @@ -101,16 +114,10 @@ def refresh_token(self, device_family, refresh_token, api_key): }, api_key=api_key, )["extensions"]["sdk"]["token"] - - -# Edge case where a bug in a working-in-progress version of -# https://github.com/psf/black/pull/3370 causes an infinite recursion. assert ( long_module.long_class.long_func().another_func() == long_module.long_class.long_func()["some_key"].another_func(arg1) ) - -# Regression test for https://github.com/psf/black/issues/3414. assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( xxxxxxxxx ).xxxxxxxxxxxxxxxxxx(), ( diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__tricky_unicode_symbols_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__tricky_unicode_symbols_py.snap new file mode 100644 index 0000000000..01c1dbbadd --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__tricky_unicode_symbols_py.snap @@ -0,0 +1,61 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/tricky_unicode_symbols.py +--- +## Input + +```py +ä = 1 +µ = 2 +蟒 = 3 +x󠄀 = 4 +មុ = 1 +Q̇_per_meter = 4 + +A᧚ = 3 +A፩ = 8 +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -4,6 +4,5 @@ + x󠄀 = 4 + មុ = 1 + Q̇_per_meter = 4 +- + A᧚ = 3 + A፩ = 8 +``` + +## Ruff Output + +```py +ä = 1 +µ = 2 +蟒 = 3 +x󠄀 = 4 +មុ = 1 +Q̇_per_meter = 4 +A᧚ = 3 +A፩ = 8 +``` + +## Black Output + +```py +ä = 1 +µ = 2 +蟒 = 3 +x󠄀 = 4 +មុ = 1 +Q̇_per_meter = 4 + +A᧚ = 3 +A፩ = 8 +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__tupleassign_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__tupleassign_py.snap index d4c4d3546f..840ef55068 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__tupleassign_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__tupleassign_py.snap @@ -20,32 +20,28 @@ this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890") ```diff --- Black +++ Ruff -@@ -1,12 +1,7 @@ - # This is a standalone comment. +@@ -1,12 +1,3 @@ +-# This is a standalone comment. -( - sdfjklsdfsjldkflkjsf, - sdfjsdfjlksdljkfsdlkf, - sdfsdjfklsdfjlksdljkf, - sdsfsdfjskdflsfsdf, -) = (1, 2, 3) -+sdfjklsdfsjldkflkjsf, sdfjsdfjlksdljkfsdlkf, sdfsdjfklsdfjlksdljkf, sdsfsdfjskdflsfsdf = 1, 2, 3 - - # This is as well. +- +-# This is as well. -(this_will_be_wrapped_in_parens,) = struct.unpack(b"12345678901234567890") +- ++sdfjklsdfsjldkflkjsf, sdfjsdfjlksdljkfsdlkf, sdfsdjfklsdfjlksdljkf, sdsfsdfjskdflsfsdf = 1, 2, 3 +this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890") - (a,) = call() ``` ## Ruff Output ```py -# This is a standalone comment. sdfjklsdfsjldkflkjsf, sdfjsdfjlksdljkfsdlkf, sdfsdjfklsdfjlksdljkf, sdsfsdfjskdflsfsdf = 1, 2, 3 - -# This is as well. this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890") - (a,) = call() ``` diff --git a/crates/ruff_python_formatter/src/statement/mod.rs b/crates/ruff_python_formatter/src/statement/mod.rs index 13bf2372ad..a330b6b5c9 100644 --- a/crates/ruff_python_formatter/src/statement/mod.rs +++ b/crates/ruff_python_formatter/src/statement/mod.rs @@ -1,3 +1,8 @@ +use crate::context::PyFormatContext; +use crate::{AsFormat, IntoFormat, PyFormatter}; +use ruff_formatter::{Format, FormatOwnedWithRule, FormatRefWithRule, FormatResult, FormatRule}; +use rustpython_parser::ast::Stmt; + pub(crate) mod stmt_ann_assign; pub(crate) mod stmt_assert; pub(crate) mod stmt_assign; @@ -25,3 +30,54 @@ pub(crate) mod stmt_try; pub(crate) mod stmt_try_star; pub(crate) mod stmt_while; pub(crate) mod stmt_with; + +#[derive(Default)] +pub struct FormatStmt; + +impl FormatRule> for FormatStmt { + fn fmt(&self, item: &Stmt, f: &mut PyFormatter) -> FormatResult<()> { + match item { + Stmt::FunctionDef(x) => x.format().fmt(f), + Stmt::AsyncFunctionDef(x) => x.format().fmt(f), + Stmt::ClassDef(x) => x.format().fmt(f), + Stmt::Return(x) => x.format().fmt(f), + Stmt::Delete(x) => x.format().fmt(f), + Stmt::Assign(x) => x.format().fmt(f), + Stmt::AugAssign(x) => x.format().fmt(f), + Stmt::AnnAssign(x) => x.format().fmt(f), + Stmt::For(x) => x.format().fmt(f), + Stmt::AsyncFor(x) => x.format().fmt(f), + Stmt::While(x) => x.format().fmt(f), + Stmt::If(x) => x.format().fmt(f), + Stmt::With(x) => x.format().fmt(f), + Stmt::AsyncWith(x) => x.format().fmt(f), + Stmt::Match(x) => x.format().fmt(f), + Stmt::Raise(x) => x.format().fmt(f), + Stmt::Try(x) => x.format().fmt(f), + Stmt::TryStar(x) => x.format().fmt(f), + Stmt::Assert(x) => x.format().fmt(f), + Stmt::Import(x) => x.format().fmt(f), + Stmt::ImportFrom(x) => x.format().fmt(f), + Stmt::Global(x) => x.format().fmt(f), + Stmt::Nonlocal(x) => x.format().fmt(f), + Stmt::Expr(x) => x.format().fmt(f), + Stmt::Pass(x) => x.format().fmt(f), + Stmt::Break(x) => x.format().fmt(f), + Stmt::Continue(x) => x.format().fmt(f), + } + } +} + +impl<'ast> AsFormat> for Stmt { + type Format<'a> = FormatRefWithRule<'a, Stmt, FormatStmt, PyFormatContext<'ast>>; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new(self, FormatStmt::default()) + } +} + +impl<'ast> IntoFormat> for Stmt { + type Format = FormatOwnedWithRule>; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new(self, FormatStmt::default()) + } +}