diff --git a/Cargo.lock b/Cargo.lock index 52a39d829f60c2..43e96c3a5a116d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2105,7 +2105,7 @@ dependencies = [ [[package]] name = "ruff_text_size" version = "0.0.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=08ebbe40d7776cac6e3ba66277d435056f2b8dca#08ebbe40d7776cac6e3ba66277d435056f2b8dca" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=f60e204b73b95bdb6ce87ccd0de34081b4a17c11#f60e204b73b95bdb6ce87ccd0de34081b4a17c11" dependencies = [ "schemars", "serde", @@ -2183,7 +2183,7 @@ dependencies = [ [[package]] name = "rustpython-ast" version = "0.2.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=08ebbe40d7776cac6e3ba66277d435056f2b8dca#08ebbe40d7776cac6e3ba66277d435056f2b8dca" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=f60e204b73b95bdb6ce87ccd0de34081b4a17c11#f60e204b73b95bdb6ce87ccd0de34081b4a17c11" dependencies = [ "is-macro", "num-bigint", @@ -2194,7 +2194,7 @@ dependencies = [ [[package]] name = "rustpython-format" version = "0.2.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=08ebbe40d7776cac6e3ba66277d435056f2b8dca#08ebbe40d7776cac6e3ba66277d435056f2b8dca" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=f60e204b73b95bdb6ce87ccd0de34081b4a17c11#f60e204b73b95bdb6ce87ccd0de34081b4a17c11" dependencies = [ "bitflags 2.3.1", "itertools", @@ -2206,7 +2206,7 @@ dependencies = [ [[package]] name = "rustpython-literal" version = "0.2.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=08ebbe40d7776cac6e3ba66277d435056f2b8dca#08ebbe40d7776cac6e3ba66277d435056f2b8dca" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=f60e204b73b95bdb6ce87ccd0de34081b4a17c11#f60e204b73b95bdb6ce87ccd0de34081b4a17c11" dependencies = [ "hexf-parse", "is-macro", @@ -2218,7 +2218,7 @@ dependencies = [ [[package]] name = "rustpython-parser" version = "0.2.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=08ebbe40d7776cac6e3ba66277d435056f2b8dca#08ebbe40d7776cac6e3ba66277d435056f2b8dca" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=f60e204b73b95bdb6ce87ccd0de34081b4a17c11#f60e204b73b95bdb6ce87ccd0de34081b4a17c11" dependencies = [ "anyhow", "is-macro", @@ -2241,7 +2241,7 @@ dependencies = [ [[package]] name = "rustpython-parser-core" version = "0.2.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=08ebbe40d7776cac6e3ba66277d435056f2b8dca#08ebbe40d7776cac6e3ba66277d435056f2b8dca" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=f60e204b73b95bdb6ce87ccd0de34081b4a17c11#f60e204b73b95bdb6ce87ccd0de34081b4a17c11" dependencies = [ "is-macro", "memchr", diff --git a/Cargo.toml b/Cargo.toml index f6b7b59b226795..c4d92051697640 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,15 +50,15 @@ toml = { version = "0.7.2" } # v0.0.1 libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "80e4c1399f95e5beb532fdd1e209ad2dbb470438" } # v0.0.3 -ruff_text_size = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "08ebbe40d7776cac6e3ba66277d435056f2b8dca" } +ruff_text_size = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "f60e204b73b95bdb6ce87ccd0de34081b4a17c11" } # v0.0.3 -rustpython-ast = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "08ebbe40d7776cac6e3ba66277d435056f2b8dca" , default-features = false, features = ["all-nodes-with-ranges", "num-bigint"]} +rustpython-ast = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "f60e204b73b95bdb6ce87ccd0de34081b4a17c11" , default-features = false, features = ["all-nodes-with-ranges", "num-bigint"]} # v0.0.3 -rustpython-format = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "08ebbe40d7776cac6e3ba66277d435056f2b8dca", default-features = false, features = ["num-bigint"] } +rustpython-format = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "f60e204b73b95bdb6ce87ccd0de34081b4a17c11", default-features = false, features = ["num-bigint"] } # v0.0.3 -rustpython-literal = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "08ebbe40d7776cac6e3ba66277d435056f2b8dca", default-features = false } +rustpython-literal = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "f60e204b73b95bdb6ce87ccd0de34081b4a17c11", default-features = false } # v0.0.3 -rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "08ebbe40d7776cac6e3ba66277d435056f2b8dca" , default-features = false, features = ["full-lexer", "all-nodes-with-ranges", "num-bigint"] } +rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "f60e204b73b95bdb6ce87ccd0de34081b4a17c11" , default-features = false, features = ["full-lexer", "all-nodes-with-ranges", "num-bigint"] } [profile.release] lto = "fat" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/class_definition.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/class_definition.py new file mode 100644 index 00000000000000..ec58f89c51d48b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/class_definition.py @@ -0,0 +1,36 @@ +class Test( + Aaaaaaaaaaaaaaaaa, + Bbbbbbbbbbbbbbbb, + DDDDDDDDDDDDDDDD, + EEEEEEEEEEEEEE, + metaclass=meta, +): + pass + + +class Test((Aaaaaaaaaaaaaaaaa), Bbbbbbbbbbbbbbbb, metaclass=meta): + pass + +class Test( # trailing class comment + Aaaaaaaaaaaaaaaaa, # trailing comment + + # in between comment + + Bbbbbbbbbbbbbbbb, + # another leading comment + DDDDDDDDDDDDDDDD, + EEEEEEEEEEEEEE, + # meta comment + metaclass=meta, # trailing meta comment +): + pass + +class Test((Aaaa)): + ... + + +class Test(aaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbb + cccccccccccccccccccccccc + dddddddddddddddddddddd + eeeeeeeee, ffffffffffffffffff, gggggggggggggggggg): + pass + +class Test(Aaaa): # trailing comment + pass diff --git a/crates/ruff_python_formatter/src/comments/placement.rs b/crates/ruff_python_formatter/src/comments/placement.rs index 5d09f245927156..b118a80ef85493 100644 --- a/crates/ruff_python_formatter/src/comments/placement.rs +++ b/crates/ruff_python_formatter/src/comments/placement.rs @@ -582,6 +582,12 @@ fn handle_trailing_end_of_line_condition_comment<'a>( .as_deref() .map(AnyNodeRef::from) .or_else(|| Some(AnyNodeRef::from(args.as_ref()))), + AnyNodeRef::StmtClassDef(StmtClassDef { + bases, keywords, .. + }) => keywords + .last() + .map(AnyNodeRef::from) + .or_else(|| bases.last().map(AnyNodeRef::from)), _ => None, }; @@ -622,8 +628,13 @@ fn handle_trailing_end_of_line_condition_comment<'a>( TokenKind::RParen => { // Skip over any closing parentheses } - _ => { - unreachable!("Only ')' or ':' should follow the condition") + TokenKind::Comma => { + // Skip over any trailing comma + } + kind => { + unreachable!( + "Only ')' or ':' should follow the condition but encountered {kind:?}" + ) } } } diff --git a/crates/ruff_python_formatter/src/expression/parentheses.rs b/crates/ruff_python_formatter/src/expression/parentheses.rs index d1506bd518a49c..c535d657457ad1 100644 --- a/crates/ruff_python_formatter/src/expression/parentheses.rs +++ b/crates/ruff_python_formatter/src/expression/parentheses.rs @@ -26,6 +26,8 @@ pub(super) fn default_expression_needs_parentheses( #[allow(clippy::if_same_then_else)] if parenthesize.is_always() { Parentheses::Always + } else if parenthesize.is_never() { + Parentheses::Never } // `Optional` or `Preserve` and expression has parentheses in source code. else if !parenthesize.is_if_breaks() && is_expression_parenthesized(node, source) { @@ -58,7 +60,11 @@ pub enum Parenthesize { /// Parenthesizes the expression only if it doesn't fit on a line. IfBreaks, + /// Always adds parentheses Always, + + /// Never adds parentheses. Parentheses are handled by the caller. + Never, } impl Parenthesize { @@ -66,6 +72,10 @@ impl Parenthesize { matches!(self, Parenthesize::Always) } + pub(crate) const fn is_never(self) -> bool { + matches!(self, Parenthesize::Never) + } + pub(crate) const fn is_if_breaks(self) -> bool { matches!(self, Parenthesize::IfBreaks) } diff --git a/crates/ruff_python_formatter/src/other/keyword.rs b/crates/ruff_python_formatter/src/other/keyword.rs index 7998efc47cb021..d93a56d66ac730 100644 --- a/crates/ruff_python_formatter/src/other/keyword.rs +++ b/crates/ruff_python_formatter/src/other/keyword.rs @@ -1,5 +1,6 @@ -use crate::{not_yet_implemented, FormatNodeRule, PyFormatter}; -use ruff_formatter::{write, Buffer, FormatResult}; +use crate::prelude::*; +use crate::FormatNodeRule; +use ruff_formatter::write; use rustpython_parser::ast::Keyword; #[derive(Default)] @@ -7,6 +8,15 @@ pub struct FormatKeyword; impl FormatNodeRule for FormatKeyword { fn fmt_fields(&self, item: &Keyword, f: &mut PyFormatter) -> FormatResult<()> { - write!(f, [not_yet_implemented(item)]) + let Keyword { + range: _, + arg, + value, + } = item; + if let Some(argument) = arg { + write!(f, [argument.format(), text("=")])?; + } + + value.format().fmt(f) } } 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 deleted file mode 100644 index 301e65440e73d8..00000000000000 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_blank_parentheses_py.snap +++ /dev/null @@ -1,135 +0,0 @@ ---- -source: crates/ruff_python_formatter/src/lib.rs -expression: snapshot -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/class_blank_parentheses.py ---- -## Input - -```py -class SimpleClassWithBlankParentheses(): - pass -class ClassWithSpaceParentheses ( ): - first_test_data = 90 - second_test_data = 100 - def test_func(self): - return None -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(): - class InsideFunc(): - pass -class NormalClass ( -): - def func_for_testing(self, first, second): - sum = first + second - return sum -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -1,18 +1,10 @@ --class SimpleClassWithBlankParentheses: -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef - - --class ClassWithSpaceParentheses: -- first_test_data = 90 -- second_test_data = 100 -- -- def test_func(self): -- return None -+NOT_YET_IMPLEMENTED_StmtClassDef - - --class ClassWithEmptyFunc(object): -- def func_with_blank_parentheses(): -- return 5 -+NOT_YET_IMPLEMENTED_StmtClassDef - - - def public_func_with_blank_parentheses(): -@@ -20,11 +12,7 @@ - - - def class_under_the_func_with_blank_parentheses(): -- class InsideFunc: -- pass -+ NOT_YET_IMPLEMENTED_StmtClassDef - - --class NormalClass: -- def func_for_testing(self, first, second): -- sum = first + second -- return sum -+NOT_YET_IMPLEMENTED_StmtClassDef -``` - -## Ruff Output - -```py -NOT_YET_IMPLEMENTED_StmtClassDef - - -NOT_YET_IMPLEMENTED_StmtClassDef - - -NOT_YET_IMPLEMENTED_StmtClassDef - - -def public_func_with_blank_parentheses(): - return None - - -def class_under_the_func_with_blank_parentheses(): - NOT_YET_IMPLEMENTED_StmtClassDef - - -NOT_YET_IMPLEMENTED_StmtClassDef -``` - -## Black Output - -```py -class SimpleClassWithBlankParentheses: - pass - - -class ClassWithSpaceParentheses: - first_test_data = 90 - second_test_data = 100 - - def test_func(self): - return None - - -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(): - class InsideFunc: - pass - - -class NormalClass: - def func_for_testing(self, first, second): - sum = first + second - return sum -``` - - diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_methods_new_line_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_methods_new_line_py.snap index ecb8824ed14958..3ea7548946d235 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_methods_new_line_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_methods_new_line_py.snap @@ -113,259 +113,257 @@ class ClassWithDecoInitAndVarsAndDocstringWithInner2: ```diff --- Black +++ Ruff -@@ -1,165 +1,61 @@ --class ClassSimplest: -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef +@@ -7,7 +7,7 @@ --class ClassWithSingleField: -- a = 1 -+NOT_YET_IMPLEMENTED_StmtClassDef - - --class ClassWithJustTheDocstring: + class ClassWithJustTheDocstring: - """Just a docstring.""" -+NOT_YET_IMPLEMENTED_StmtClassDef ++ "NOT_YET_IMPLEMENTED_STRING" --class ClassWithInit: -- def __init__(self): -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef + class ClassWithInit: +@@ -16,7 +16,7 @@ --class ClassWithTheDocstringAndInit: + class ClassWithTheDocstringAndInit: - """Just a docstring.""" -+NOT_YET_IMPLEMENTED_StmtClassDef - -- def __init__(self): -- pass - -+NOT_YET_IMPLEMENTED_StmtClassDef - --class ClassWithInitAndVars: -- cls_var = 100 ++ "NOT_YET_IMPLEMENTED_STRING" -- def __init__(self): -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef + def __init__(self): + pass +@@ -30,8 +30,7 @@ --class ClassWithInitAndVarsAndDocstring: + class ClassWithInitAndVarsAndDocstring: - """Test class""" -+NOT_YET_IMPLEMENTED_StmtClassDef - -- cls_var = 100 - -- def __init__(self): -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef - - --class ClassWithDecoInit: -- @deco -- def __init__(self): -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef - - --class ClassWithDecoInitAndVars: -- cls_var = 100 -+NOT_YET_IMPLEMENTED_StmtClassDef +- ++ "NOT_YET_IMPLEMENTED_STRING" + cls_var = 100 -- @deco -- def __init__(self): -- pass + def __init__(self): +@@ -53,8 +52,7 @@ -+NOT_YET_IMPLEMENTED_StmtClassDef --class ClassWithDecoInitAndVarsAndDocstring: + class ClassWithDecoInitAndVarsAndDocstring: - """Test class""" +- ++ "NOT_YET_IMPLEMENTED_STRING" + cls_var = 100 -- cls_var = 100 -+NOT_YET_IMPLEMENTED_StmtClassDef - -- @deco -- def __init__(self): -- pass - -+NOT_YET_IMPLEMENTED_StmtClassDef - --class ClassSimplestWithInner: -- class Inner: -- pass - -+NOT_YET_IMPLEMENTED_StmtClassDef + @deco +@@ -69,7 +67,7 @@ --class ClassSimplestWithInnerWithDocstring: -- class Inner: + class ClassSimplestWithInnerWithDocstring: + class Inner: - """Just a docstring.""" ++ "NOT_YET_IMPLEMENTED_STRING" -- def __init__(self): -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef - - --class ClassWithSingleFieldWithInner: -- a = 1 -+NOT_YET_IMPLEMENTED_StmtClassDef - -- class Inner: -- pass + def __init__(self): + pass +@@ -83,7 +81,7 @@ -+NOT_YET_IMPLEMENTED_StmtClassDef --class ClassWithJustTheDocstringWithInner: + class ClassWithJustTheDocstringWithInner: - """Just a docstring.""" ++ "NOT_YET_IMPLEMENTED_STRING" -- class Inner: -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef + class Inner: + pass +@@ -108,8 +106,7 @@ --class ClassWithInitWithInner: -- class Inner: -- pass -- -- def __init__(self): -- pass -- -- --class ClassWithInitAndVarsWithInner: -- cls_var = 100 -- -- class Inner: -- pass -- -- def __init__(self): -- pass -- -- --class ClassWithInitAndVarsAndDocstringWithInner: + class ClassWithInitAndVarsAndDocstringWithInner: - """Test class""" - -- cls_var = 100 -- -- class Inner: -- pass -- -- def __init__(self): -- pass -- -- --class ClassWithDecoInitWithInner: -- class Inner: -- pass -- -- @deco -- def __init__(self): -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef ++ "NOT_YET_IMPLEMENTED_STRING" + cls_var = 100 + class Inner: +@@ -140,8 +137,7 @@ --class ClassWithDecoInitAndVarsWithInner: -- cls_var = 100 -- -- class Inner: -- pass -- -- @deco -- def __init__(self): -- pass -- -- --class ClassWithDecoInitAndVarsAndDocstringWithInner: + + class ClassWithDecoInitAndVarsAndDocstringWithInner: - """Test class""" - -- cls_var = 100 -- -- class Inner: -- pass -- -- @deco -- def __init__(self): -- pass -- -- --class ClassWithDecoInitAndVarsAndDocstringWithInner2: ++ "NOT_YET_IMPLEMENTED_STRING" + cls_var = 100 + + class Inner: +@@ -153,7 +149,7 @@ + + + class ClassWithDecoInitAndVarsAndDocstringWithInner2: - """Test class""" -- -- class Inner: -- pass -- -- cls_var = 100 -- -- @deco -- def __init__(self): -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef ++ "NOT_YET_IMPLEMENTED_STRING" + + class Inner: + pass ``` ## Ruff Output ```py -NOT_YET_IMPLEMENTED_StmtClassDef +class ClassSimplest: + pass + + +class ClassWithSingleField: + a = 1 + + +class ClassWithJustTheDocstring: + "NOT_YET_IMPLEMENTED_STRING" + + +class ClassWithInit: + def __init__(self): + pass + + +class ClassWithTheDocstringAndInit: + "NOT_YET_IMPLEMENTED_STRING" + + def __init__(self): + pass + + +class ClassWithInitAndVars: + cls_var = 100 + + def __init__(self): + pass + + +class ClassWithInitAndVarsAndDocstring: + "NOT_YET_IMPLEMENTED_STRING" + cls_var = 100 + + def __init__(self): + pass + + +class ClassWithDecoInit: + @deco + def __init__(self): + pass + + +class ClassWithDecoInitAndVars: + cls_var = 100 + + @deco + def __init__(self): + pass -NOT_YET_IMPLEMENTED_StmtClassDef +class ClassWithDecoInitAndVarsAndDocstring: + "NOT_YET_IMPLEMENTED_STRING" + cls_var = 100 + + @deco + def __init__(self): + pass -NOT_YET_IMPLEMENTED_StmtClassDef +class ClassSimplestWithInner: + class Inner: + pass -NOT_YET_IMPLEMENTED_StmtClassDef +class ClassSimplestWithInnerWithDocstring: + class Inner: + "NOT_YET_IMPLEMENTED_STRING" + def __init__(self): + pass -NOT_YET_IMPLEMENTED_StmtClassDef +class ClassWithSingleFieldWithInner: + a = 1 -NOT_YET_IMPLEMENTED_StmtClassDef + class Inner: + pass -NOT_YET_IMPLEMENTED_StmtClassDef +class ClassWithJustTheDocstringWithInner: + "NOT_YET_IMPLEMENTED_STRING" + class Inner: + pass -NOT_YET_IMPLEMENTED_StmtClassDef +class ClassWithInitWithInner: + class Inner: + pass -NOT_YET_IMPLEMENTED_StmtClassDef + def __init__(self): + pass -NOT_YET_IMPLEMENTED_StmtClassDef +class ClassWithInitAndVarsWithInner: + cls_var = 100 + class Inner: + pass -NOT_YET_IMPLEMENTED_StmtClassDef + def __init__(self): + pass -NOT_YET_IMPLEMENTED_StmtClassDef +class ClassWithInitAndVarsAndDocstringWithInner: + "NOT_YET_IMPLEMENTED_STRING" + cls_var = 100 + class Inner: + pass -NOT_YET_IMPLEMENTED_StmtClassDef + def __init__(self): + pass -NOT_YET_IMPLEMENTED_StmtClassDef +class ClassWithDecoInitWithInner: + class Inner: + pass + @deco + def __init__(self): + pass -NOT_YET_IMPLEMENTED_StmtClassDef +class ClassWithDecoInitAndVarsWithInner: + cls_var = 100 -NOT_YET_IMPLEMENTED_StmtClassDef + class Inner: + pass + @deco + def __init__(self): + pass -NOT_YET_IMPLEMENTED_StmtClassDef +class ClassWithDecoInitAndVarsAndDocstringWithInner: + "NOT_YET_IMPLEMENTED_STRING" + cls_var = 100 -NOT_YET_IMPLEMENTED_StmtClassDef + class Inner: + pass + @deco + def __init__(self): + pass -NOT_YET_IMPLEMENTED_StmtClassDef +class ClassWithDecoInitAndVarsAndDocstringWithInner2: + "NOT_YET_IMPLEMENTED_STRING" -NOT_YET_IMPLEMENTED_StmtClassDef + class Inner: + pass + cls_var = 100 -NOT_YET_IMPLEMENTED_StmtClassDef + @deco + def __init__(self): + pass ``` ## Black Output 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 42acde710af311..a3129fc7353533 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 @@ -349,7 +349,7 @@ instruction()#comment with bad spacing while True: if False: continue -@@ -141,25 +111,13 @@ +@@ -141,24 +111,18 @@ # and round and round we go # let's return @@ -370,15 +370,17 @@ instruction()#comment with bad spacing +CONFIG_FILES = [CONFIG_FILE] + SHARED_CONFIG_FILES + USER_CONFIG_FILES # type: Final --class Test: -- def _init_host(self, parsed) -> None: + class Test: + def _init_host(self, parsed) -> None: - if parsed.hostname is None or not parsed.hostname.strip(): # type: ignore -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef ++ if ( ++ NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right # type: ignore ++ or not NOT_IMPLEMENTED_call() ++ ): + pass - ####################### -@@ -167,7 +125,7 @@ +@@ -167,7 +131,7 @@ ####################### @@ -511,7 +513,13 @@ def inline_comments_in_brackets_ruin_everything(): CONFIG_FILES = [CONFIG_FILE] + SHARED_CONFIG_FILES + USER_CONFIG_FILES # type: Final -NOT_YET_IMPLEMENTED_StmtClassDef +class Test: + def _init_host(self, parsed) -> None: + if ( + NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right # type: ignore + or not NOT_IMPLEMENTED_call() + ): + pass ####################### 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 index 9d741d8b926d46..06fb9dfb6aef3c 100644 --- 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 @@ -107,7 +107,7 @@ def foo3(list_a, list_b): ```diff --- Black +++ Ruff -@@ -1,94 +1,22 @@ +@@ -1,94 +1,28 @@ -from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( - MyLovelyCompanyTeamProjectComponent, # NOT DRY -) @@ -118,7 +118,7 @@ def foo3(list_a, list_b): +NOT_YET_IMPLEMENTED_StmtImportFrom --class C: + class C: - @pytest.mark.parametrize( - ("post_data", "message"), - [ @@ -161,12 +161,14 @@ def foo3(list_a, list_b): - ), - ], - ) -- def test_fails_invalid_post_data( -- self, pyramid_config, db_request, post_data, message -- ): ++ @NOT_IMPLEMENTED_call() + 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) -+NOT_YET_IMPLEMENTED_StmtClassDef ++ NOT_IMPLEMENTED_call() ++ db_request.POST = NOT_IMPLEMENTED_call() def foo(list_a, list_b): @@ -217,7 +219,13 @@ NOT_YET_IMPLEMENTED_StmtImportFrom NOT_YET_IMPLEMENTED_StmtImportFrom -NOT_YET_IMPLEMENTED_StmtClassDef +class C: + @NOT_IMPLEMENTED_call() + def test_fails_invalid_post_data( + self, pyramid_config, db_request, post_data, message + ): + NOT_IMPLEMENTED_call() + db_request.POST = NOT_IMPLEMENTED_call() def foo(list_a, list_b): 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 cddd66b042c0b0..23a46d6cd863e3 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,38 +152,16 @@ def bar(): ```diff --- Black +++ Ruff -@@ -30,8 +30,7 @@ +@@ -44,7 +44,7 @@ - # This comment should be split from the statement above by two lines. --class MyClass: -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef - - - some = statement -@@ -39,17 +38,14 @@ - - - # This should be split from the above by two lines --class MyClassWithComplexLeadingComments: -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef - - --class ClassWithDocstring: + class ClassWithDocstring: - """A docstring.""" -+NOT_YET_IMPLEMENTED_StmtClassDef ++ "NOT_YET_IMPLEMENTED_STRING" # Leading comment after a class with just a docstring --class MyClassAfterAnotherClassWithDocstring: -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef - - - some = statement -@@ -59,7 +55,7 @@ +@@ -59,7 +59,7 @@ @deco1 # leading 2 # leading 2 extra @@ -192,7 +170,7 @@ def bar(): # leading 3 @deco3 # leading 4 -@@ -73,7 +69,7 @@ +@@ -73,7 +73,7 @@ # leading 1 @deco1 # leading 2 @@ -201,7 +179,7 @@ def bar(): # leading 3 that already has an empty line @deco3 -@@ -88,7 +84,7 @@ +@@ -88,7 +88,7 @@ # leading 1 @deco1 # leading 2 @@ -210,7 +188,7 @@ def bar(): # leading 3 @deco3 -@@ -106,7 +102,6 @@ +@@ -106,7 +106,6 @@ # Another leading comment def another_inline(): pass @@ -218,7 +196,7 @@ def bar(): else: # More leading comments def inline_after_else(): -@@ -121,18 +116,13 @@ +@@ -121,7 +120,6 @@ # Another leading comment def another_top_level_quote_inline_inline(): pass @@ -226,18 +204,6 @@ def bar(): 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 -+NOT_YET_IMPLEMENTED_StmtClassDef - - - # Regression test for https://github.com/psf/black/issues/3454. ``` ## Ruff Output @@ -275,7 +241,8 @@ some = statement # This comment should be split from the statement above by two lines. -NOT_YET_IMPLEMENTED_StmtClassDef +class MyClass: + pass some = statement @@ -283,14 +250,17 @@ some = statement # This should be split from the above by two lines -NOT_YET_IMPLEMENTED_StmtClassDef +class MyClassWithComplexLeadingComments: + pass -NOT_YET_IMPLEMENTED_StmtClassDef +class ClassWithDocstring: + "NOT_YET_IMPLEMENTED_STRING" # Leading comment after a class with just a docstring -NOT_YET_IMPLEMENTED_StmtClassDef +class MyClassAfterAnotherClassWithDocstring: + pass some = statement @@ -367,7 +337,11 @@ else: pass -NOT_YET_IMPLEMENTED_StmtClassDef +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. 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 e9ed496189e9a4..3f9359d5932de3 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 @@ -114,16 +114,16 @@ async def wat(): # Has many lines. Many, many lines. # Many, many, many lines. -"""Module docstring. -- --Possibly also many, many lines. --""" +"NOT_YET_IMPLEMENTED_STRING" --import os.path --import sys +-Possibly also many, many lines. +-""" +NOT_YET_IMPLEMENTED_StmtImport +NOT_YET_IMPLEMENTED_StmtImport +-import os.path +-import sys +- -import a -from b.c import X # some noqa comment +NOT_YET_IMPLEMENTED_StmtImport @@ -137,7 +137,7 @@ async def wat(): # Some comment before a function. -@@ -30,67 +24,50 @@ +@@ -30,25 +24,26 @@ def function(default=None): @@ -173,28 +173,29 @@ async def wat(): # Another comment! - # This time two lines. +@@ -56,7 +51,7 @@ --class Foo: + class Foo: - """Docstring for class Foo. Example from Sphinx docs.""" -- -- #: Doc comment for class attribute Foo.bar. -- #: It can have multiple lines. -- bar = 1 -- -- flox = 1.5 #: Doc comment for Foo.flox. One line only. -- -- baz = 2 ++ "NOT_YET_IMPLEMENTED_STRING" + + #: Doc comment for class attribute Foo.bar. + #: It can have multiple lines. +@@ -65,32 +60,31 @@ + flox = 1.5 #: Doc comment for Foo.flox. One line only. + + baz = 2 - """Docstring for class attribute Foo.baz.""" -- -- def __init__(self): -- #: Doc comment for instance attribute qux. -- self.qux = 3 -- -- self.spam = 4 ++ "NOT_YET_IMPLEMENTED_STRING" + + def __init__(self): + #: Doc comment for instance attribute qux. + self.qux = 3 + + self.spam = 4 - """Docstring for instance attribute spam.""" -+NOT_YET_IMPLEMENTED_StmtClassDef ++ "NOT_YET_IMPLEMENTED_STRING" #'

This is pweave!

@@ -279,7 +280,24 @@ GLOBAL_STATE = { # This time two lines. -NOT_YET_IMPLEMENTED_StmtClassDef +class Foo: + "NOT_YET_IMPLEMENTED_STRING" + + #: Doc comment for class attribute Foo.bar. + #: It can have multiple lines. + bar = 1 + + flox = 1.5 #: Doc comment for Foo.flox. One line only. + + baz = 2 + "NOT_YET_IMPLEMENTED_STRING" + + def __init__(self): + #: Doc comment for instance attribute qux. + self.qux = 3 + + self.spam = 4 + "NOT_YET_IMPLEMENTED_STRING" #'

This is pweave!

diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_no_trailing_comma_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_no_trailing_comma_py.snap index b45f26eec303aa..bbcaf45b2f4da3 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_no_trailing_comma_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_no_trailing_comma_py.snap @@ -194,9 +194,9 @@ class C: ```diff --- Black +++ Ruff -@@ -1,181 +1 @@ --class C: -- def test(self) -> None: +@@ -1,181 +1,44 @@ + class C: + def test(self) -> None: - with patch("black.out", print): - self.assertEqual( - unstyle(str(report)), "1 file reformatted, 1 file failed to reformat." @@ -239,23 +239,36 @@ class C: - return ( - 'Utterly failed doctest test for %s\n File "%s", line %s, in %s\n\n%s' - % (test.name, test.filename, lineno, lname, err) -- ) -- -- def omitting_trailers(self) -> None: ++ NOT_YET_IMPLEMENTED_StmtWith ++ xxxxxxxxxxxxxxxx = NOT_IMPLEMENTED_call() ++ return "NOT_YET_IMPLEMENTED_STRING" % ( ++ test.name, ++ test.filename, ++ lineno, ++ lname, ++ err, + ) + + def omitting_trailers(self) -> None: - get_collection( - hey_this_is_a_very_long_call, it_has_funny_attributes, really=True - )[OneLevelIndex] - get_collection( - hey_this_is_a_very_long_call, it_has_funny_attributes, really=True - )[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][FourLevelIndex] -- d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ -- 22 -- ] ++ NOT_IMPLEMENTED_call()[OneLevelIndex] ++ NOT_IMPLEMENTED_call()[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][ ++ FourLevelIndex ++ ] + d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ + 22 + ] - assignment = ( - some.rather.elaborate.rule() and another.rule.ending_with.index[123] - ) -- -- def easy_asserts(self) -> None: ++ assignment = NOT_IMPLEMENTED_call() and another.rule.ending_with.index[123] + + def easy_asserts(self) -> None: - assert { - key1: value1, - key2: value2, @@ -267,7 +280,8 @@ class C: - key8: value8, - key9: value9, - } == expected, "Not what we expected" -- ++ NOT_YET_IMPLEMENTED_StmtAssert + - assert expected == { - key1: value1, - key2: value2, @@ -279,7 +293,8 @@ class C: - key8: value8, - key9: value9, - }, "Not what we expected" -- ++ NOT_YET_IMPLEMENTED_StmtAssert + - assert expected == { - key1: value1, - key2: value2, @@ -291,8 +306,9 @@ class C: - key8: value8, - key9: value9, - } -- -- def tricky_asserts(self) -> None: ++ NOT_YET_IMPLEMENTED_StmtAssert + + def tricky_asserts(self) -> None: - assert { - key1: value1, - key2: value2, @@ -306,7 +322,8 @@ class C: - } == expected( - value, is_going_to_be="too long to fit in a single line", srsly=True - ), "Not what we expected" -- ++ NOT_YET_IMPLEMENTED_StmtAssert + - assert { - key1: value1, - key2: value2, @@ -320,7 +337,8 @@ class C: - } == expected, ( - "Not what we expected and the message is too long to fit in one line" - ) -- ++ NOT_YET_IMPLEMENTED_StmtAssert + - assert expected( - value, is_going_to_be="too long to fit in a single line", srsly=True - ) == { @@ -334,7 +352,8 @@ class C: - key8: value8, - key9: value9, - }, "Not what we expected" -- ++ NOT_YET_IMPLEMENTED_StmtAssert + - assert expected == { - key1: value1, - key2: value2, @@ -349,7 +368,8 @@ class C: - "Not what we expected and the message is too long to fit in one line" - " because it's too long" - ) -- ++ NOT_YET_IMPLEMENTED_StmtAssert + - dis_c_instance_method = """\ - %3d 0 LOAD_FAST 1 (x) - 2 LOAD_CONST 1 (1) @@ -360,8 +380,11 @@ class C: - 12 RETURN_VALUE - """ % ( - _C.__init__.__code__.co_firstlineno + 1, -- ) -- ++ dis_c_instance_method = "NOT_YET_IMPLEMENTED_STRING" % ( ++ _C.__init__.__code__.co_firstlineno ++ + 1, + ) + - assert ( - expectedexpectedexpectedexpectedexpectedexpectedexpectedexpectedexpect - == { @@ -376,13 +399,56 @@ class C: - key9: value9, - } - ) -+NOT_YET_IMPLEMENTED_StmtClassDef ++ NOT_YET_IMPLEMENTED_StmtAssert ``` ## Ruff Output ```py -NOT_YET_IMPLEMENTED_StmtClassDef +class C: + def test(self) -> None: + NOT_YET_IMPLEMENTED_StmtWith + xxxxxxxxxxxxxxxx = NOT_IMPLEMENTED_call() + return "NOT_YET_IMPLEMENTED_STRING" % ( + test.name, + test.filename, + lineno, + lname, + err, + ) + + def omitting_trailers(self) -> None: + NOT_IMPLEMENTED_call()[OneLevelIndex] + NOT_IMPLEMENTED_call()[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][ + FourLevelIndex + ] + d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ + 22 + ] + assignment = NOT_IMPLEMENTED_call() and another.rule.ending_with.index[123] + + def easy_asserts(self) -> None: + NOT_YET_IMPLEMENTED_StmtAssert + + NOT_YET_IMPLEMENTED_StmtAssert + + NOT_YET_IMPLEMENTED_StmtAssert + + def tricky_asserts(self) -> None: + NOT_YET_IMPLEMENTED_StmtAssert + + NOT_YET_IMPLEMENTED_StmtAssert + + NOT_YET_IMPLEMENTED_StmtAssert + + NOT_YET_IMPLEMENTED_StmtAssert + + dis_c_instance_method = "NOT_YET_IMPLEMENTED_STRING" % ( + _C.__init__.__code__.co_firstlineno + + 1, + ) + + NOT_YET_IMPLEMENTED_StmtAssert ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_py.snap index 86812da5ea8cfc..5376626db65c8e 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_py.snap @@ -194,9 +194,9 @@ class C: ```diff --- Black +++ Ruff -@@ -1,181 +1 @@ --class C: -- def test(self) -> None: +@@ -1,181 +1,44 @@ + class C: + def test(self) -> None: - with patch("black.out", print): - self.assertEqual( - unstyle(str(report)), "1 file reformatted, 1 file failed to reformat." @@ -239,23 +239,36 @@ class C: - return ( - 'Utterly failed doctest test for %s\n File "%s", line %s, in %s\n\n%s' - % (test.name, test.filename, lineno, lname, err) -- ) -- -- def omitting_trailers(self) -> None: ++ NOT_YET_IMPLEMENTED_StmtWith ++ xxxxxxxxxxxxxxxx = NOT_IMPLEMENTED_call() ++ return "NOT_YET_IMPLEMENTED_STRING" % ( ++ test.name, ++ test.filename, ++ lineno, ++ lname, ++ err, + ) + + def omitting_trailers(self) -> None: - get_collection( - hey_this_is_a_very_long_call, it_has_funny_attributes, really=True - )[OneLevelIndex] - get_collection( - hey_this_is_a_very_long_call, it_has_funny_attributes, really=True - )[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][FourLevelIndex] -- d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ -- 22 -- ] ++ NOT_IMPLEMENTED_call()[OneLevelIndex] ++ NOT_IMPLEMENTED_call()[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][ ++ FourLevelIndex ++ ] + d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ + 22 + ] - assignment = ( - some.rather.elaborate.rule() and another.rule.ending_with.index[123] - ) -- -- def easy_asserts(self) -> None: ++ assignment = NOT_IMPLEMENTED_call() and another.rule.ending_with.index[123] + + def easy_asserts(self) -> None: - assert { - key1: value1, - key2: value2, @@ -267,7 +280,8 @@ class C: - key8: value8, - key9: value9, - } == expected, "Not what we expected" -- ++ NOT_YET_IMPLEMENTED_StmtAssert + - assert expected == { - key1: value1, - key2: value2, @@ -279,7 +293,8 @@ class C: - key8: value8, - key9: value9, - }, "Not what we expected" -- ++ NOT_YET_IMPLEMENTED_StmtAssert + - assert expected == { - key1: value1, - key2: value2, @@ -291,8 +306,9 @@ class C: - key8: value8, - key9: value9, - } -- -- def tricky_asserts(self) -> None: ++ NOT_YET_IMPLEMENTED_StmtAssert + + def tricky_asserts(self) -> None: - assert { - key1: value1, - key2: value2, @@ -306,7 +322,8 @@ class C: - } == expected( - value, is_going_to_be="too long to fit in a single line", srsly=True - ), "Not what we expected" -- ++ NOT_YET_IMPLEMENTED_StmtAssert + - assert { - key1: value1, - key2: value2, @@ -320,7 +337,8 @@ class C: - } == expected, ( - "Not what we expected and the message is too long to fit in one line" - ) -- ++ NOT_YET_IMPLEMENTED_StmtAssert + - assert expected( - value, is_going_to_be="too long to fit in a single line", srsly=True - ) == { @@ -334,7 +352,8 @@ class C: - key8: value8, - key9: value9, - }, "Not what we expected" -- ++ NOT_YET_IMPLEMENTED_StmtAssert + - assert expected == { - key1: value1, - key2: value2, @@ -349,7 +368,8 @@ class C: - "Not what we expected and the message is too long to fit in one line" - " because it's too long" - ) -- ++ NOT_YET_IMPLEMENTED_StmtAssert + - dis_c_instance_method = """\ - %3d 0 LOAD_FAST 1 (x) - 2 LOAD_CONST 1 (1) @@ -360,8 +380,11 @@ class C: - 12 RETURN_VALUE - """ % ( - _C.__init__.__code__.co_firstlineno + 1, -- ) -- ++ dis_c_instance_method = "NOT_YET_IMPLEMENTED_STRING" % ( ++ _C.__init__.__code__.co_firstlineno ++ + 1, + ) + - assert ( - expectedexpectedexpectedexpectedexpectedexpectedexpectedexpectedexpect - == { @@ -376,13 +399,56 @@ class C: - key9: value9, - } - ) -+NOT_YET_IMPLEMENTED_StmtClassDef ++ NOT_YET_IMPLEMENTED_StmtAssert ``` ## Ruff Output ```py -NOT_YET_IMPLEMENTED_StmtClassDef +class C: + def test(self) -> None: + NOT_YET_IMPLEMENTED_StmtWith + xxxxxxxxxxxxxxxx = NOT_IMPLEMENTED_call() + return "NOT_YET_IMPLEMENTED_STRING" % ( + test.name, + test.filename, + lineno, + lname, + err, + ) + + def omitting_trailers(self) -> None: + NOT_IMPLEMENTED_call()[OneLevelIndex] + NOT_IMPLEMENTED_call()[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][ + FourLevelIndex + ] + d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ + 22 + ] + assignment = NOT_IMPLEMENTED_call() and another.rule.ending_with.index[123] + + def easy_asserts(self) -> None: + NOT_YET_IMPLEMENTED_StmtAssert + + NOT_YET_IMPLEMENTED_StmtAssert + + NOT_YET_IMPLEMENTED_StmtAssert + + def tricky_asserts(self) -> None: + NOT_YET_IMPLEMENTED_StmtAssert + + NOT_YET_IMPLEMENTED_StmtAssert + + NOT_YET_IMPLEMENTED_StmtAssert + + NOT_YET_IMPLEMENTED_StmtAssert + + dis_c_instance_method = "NOT_YET_IMPLEMENTED_STRING" % ( + _C.__init__.__code__.co_firstlineno + + 1, + ) + + NOT_YET_IMPLEMENTED_StmtAssert ``` ## 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 index 69d1e86ad391d6..d56f37ee2584c5 100644 --- 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 @@ -17,12 +17,12 @@ class ClassWithDocstring: ```diff --- Black +++ Ruff -@@ -1,4 +1,3 @@ +@@ -1,4 +1,4 @@ # Make sure when the file ends with class's docstring, # It doesn't add extra blank lines. --class ClassWithDocstring: + class ClassWithDocstring: - """A docstring.""" -+NOT_YET_IMPLEMENTED_StmtClassDef ++ "NOT_YET_IMPLEMENTED_STRING" ``` ## Ruff Output @@ -30,7 +30,8 @@ class ClassWithDocstring: ```py # Make sure when the file ends with class's docstring, # It doesn't add extra blank lines. -NOT_YET_IMPLEMENTED_StmtClassDef +class ClassWithDocstring: + "NOT_YET_IMPLEMENTED_STRING" ``` ## Black Output 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 ca085ed0883cb4..784587197dcb9e 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,18 +234,19 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): ```diff --- Black +++ Ruff -@@ -1,219 +1,149 @@ --class MyClass: +@@ -1,219 +1,154 @@ + class MyClass: - """Multiline - class docstring - """ -- -- def method(self): ++ "NOT_YET_IMPLEMENTED_STRING" + + def method(self): - """Multiline - method docstring - """ -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef ++ "NOT_YET_IMPLEMENTED_STRING" + pass def foo(): @@ -396,12 +397,12 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): - 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. -- -- line ends with some tabs -- """ + "NOT_YET_IMPLEMENTED_STRING" +- line ends with some tabs +- """ +- def docstring_with_inline_tabs_and_tab_indentation(): - """hey - @@ -494,7 +495,12 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): ## Ruff Output ```py -NOT_YET_IMPLEMENTED_StmtClassDef +class MyClass: + "NOT_YET_IMPLEMENTED_STRING" + + def method(self): + "NOT_YET_IMPLEMENTED_STRING" + pass def foo(): 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 aada9db6c5ad44..f8ce20cf9d9c60 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 @@ -134,7 +134,7 @@ elif unformatted: return True # yapf: enable elif b: -@@ -39,49 +21,29 @@ +@@ -39,12 +21,12 @@ # Regression test for https://github.com/psf/black/issues/2567. if True: # fmt: off @@ -152,40 +152,44 @@ elif unformatted: # Regression test for https://github.com/psf/black/issues/3184. --class A: -- async def call(param): -- if param: -- # fmt: off +@@ -52,29 +34,27 @@ + async def call(param): + if param: + # fmt: off - if param[0:4] in ( - "ABCD", "EFGH" - ) : -- # fmt: on ++ if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right: + # fmt: on - print ( "This won't be formatted" ) -- ++ NOT_IMPLEMENTED_call() + - elif param[0:4] in ("ZZZZ",): - print ( "This won't be formatted either" ) -- ++ elif NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right: ++ NOT_IMPLEMENTED_call() + - print("This will be formatted") -+NOT_YET_IMPLEMENTED_StmtClassDef ++ NOT_IMPLEMENTED_call() # Regression test for https://github.com/psf/black/issues/2985. --class Named(t.Protocol): -- # fmt: off -- @property + class Named(t.Protocol): + # fmt: off + @property - def this_wont_be_formatted ( self ) -> str: ... -+NOT_YET_IMPLEMENTED_StmtClassDef ++ def this_wont_be_formatted(self) -> str: ++ ... --class Factory(t.Protocol): -- def this_will_be_formatted(self, **kwargs) -> Named: -- ... + class Factory(t.Protocol): + def this_will_be_formatted(self, **kwargs) -> Named: + ... - -- # fmt: on -+NOT_YET_IMPLEMENTED_StmtClassDef + # fmt: on - # Regression test for https://github.com/psf/black/issues/3436. +@@ -82,6 +62,6 @@ if x: return x # fmt: off @@ -231,14 +235,32 @@ else: # Regression test for https://github.com/psf/black/issues/3184. -NOT_YET_IMPLEMENTED_StmtClassDef +class A: + async def call(param): + if param: + # fmt: off + if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right: + # fmt: on + NOT_IMPLEMENTED_call() + + elif NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right: + NOT_IMPLEMENTED_call() + + NOT_IMPLEMENTED_call() # Regression test for https://github.com/psf/black/issues/2985. -NOT_YET_IMPLEMENTED_StmtClassDef +class Named(t.Protocol): + # fmt: off + @property + def this_wont_be_formatted(self) -> str: + ... -NOT_YET_IMPLEMENTED_StmtClassDef +class Factory(t.Protocol): + def this_will_be_formatted(self, **kwargs) -> Named: + ... + # fmt: on # Regression test for https://github.com/psf/black/issues/3436. 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 index ea2bbeca77b836..bffd6374cb99b2 100644 --- 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 @@ -18,19 +18,23 @@ class A: ```diff --- Black +++ Ruff -@@ -1,5 +1 @@ --class A: -- def f(self): +@@ -1,5 +1,5 @@ + class A: + def f(self): - for line in range(10): -- if True: -- pass # fmt: skip -+NOT_YET_IMPLEMENTED_StmtClassDef ++ for line in NOT_IMPLEMENTED_call(): + if True: + pass # fmt: skip ``` ## Ruff Output ```py -NOT_YET_IMPLEMENTED_StmtClassDef +class A: + def f(self): + for line in NOT_IMPLEMENTED_call(): + if True: + pass # fmt: skip ``` ## 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 index eb9f51311f606e..aec767ded17d77 100644 --- 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 @@ -75,7 +75,7 @@ async def test_async_with(): ```diff --- Black +++ Ruff -@@ -1,62 +1,47 @@ +@@ -1,62 +1,54 @@ # Make sure a leading comment is not removed. -def some_func( unformatted, args ): # fmt: skip - print("I am some_func") @@ -98,14 +98,19 @@ async def test_async_with(): -class SomeClass( Unformatted, SuperClasses ): # fmt: skip - def some_method( self, unformatted, args ): # fmt: skip - print("I am some_method") -- return 0 -+NOT_YET_IMPLEMENTED_StmtClassDef ++class SomeClass(Unformatted, SuperClasses): # fmt: skip ++ def some_method(self, unformatted, args): # fmt: skip ++ NOT_IMPLEMENTED_call() + return 0 - async def some_async_method( self, unformatted, args ): # fmt: skip - print("I am some_async_method") - await asyncio.sleep(1) ++ async def some_async_method(self, unformatted, args): # fmt: skip ++ NOT_IMPLEMENTED_call() ++ await NOT_IMPLEMENTED_call() + -- # Make sure a leading comment is not removed. -if unformatted_call( args ): # fmt: skip - print("First branch") @@ -177,7 +182,14 @@ async def some_async_func(unformatted, args): # fmt: skip # Make sure a leading comment is not removed. -NOT_YET_IMPLEMENTED_StmtClassDef +class SomeClass(Unformatted, SuperClasses): # fmt: skip + def some_method(self, unformatted, args): # fmt: skip + NOT_IMPLEMENTED_call() + return 0 + + async def some_async_method(self, unformatted, args): # fmt: skip + NOT_IMPLEMENTED_call() + await NOT_IMPLEMENTED_call() # Make sure a leading comment is not removed. 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 44b9556da971d0..d3e2515919a087 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 @@ -66,7 +66,7 @@ with hmm_but_this_should_get_two_preceding_newlines(): ```diff --- Black +++ Ruff -@@ -2,64 +2,39 @@ +@@ -2,64 +2,41 @@ a, **kwargs, ) -> A: @@ -129,17 +129,17 @@ with hmm_but_this_should_get_two_preceding_newlines(): elif False: - -- class IHopeYouAreHavingALovelyDay: -- def __call__(self): + class IHopeYouAreHavingALovelyDay: + def __call__(self): - print("i_should_be_followed_by_only_one_newline") - -+ NOT_YET_IMPLEMENTED_StmtClassDef ++ NOT_IMPLEMENTED_call() else: - def foo(): pass - - + -with hmm_but_this_should_get_two_preceding_newlines(): - pass +NOT_YET_IMPLEMENTED_StmtWith @@ -182,7 +182,9 @@ elif NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right: NOT_YET_IMPLEMENTED_StmtTry elif False: - NOT_YET_IMPLEMENTED_StmtClassDef + class IHopeYouAreHavingALovelyDay: + def __call__(self): + NOT_IMPLEMENTED_call() else: def foo(): pass 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 642e62ce922ed3..69f7cec332ae45 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,7 +121,7 @@ with open("/path/to/file.txt", mode="r") as read_file: ```diff --- Black +++ Ruff -@@ -1,78 +1,72 @@ +@@ -1,78 +1,74 @@ -import random +NOT_YET_IMPLEMENTED_StmtImport @@ -151,10 +151,10 @@ with open("/path/to/file.txt", mode="r") as read_file: + NOT_IMPLEMENTED_call() --class Foo: -- def bar(self): + class Foo: + def bar(self): - print("The newline above me should be deleted!") -+NOT_YET_IMPLEMENTED_StmtClassDef ++ NOT_IMPLEMENTED_call() -for i in range(5): @@ -255,7 +255,9 @@ def foo4(): NOT_IMPLEMENTED_call() -NOT_YET_IMPLEMENTED_StmtClassDef +class Foo: + def bar(self): + NOT_IMPLEMENTED_call() for i in NOT_IMPLEMENTED_call(): 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 e9d41df04d83ed..899a8782bd6496 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,7 +42,7 @@ assert ( ```diff --- Black +++ Ruff -@@ -2,57 +2,24 @@ +@@ -2,20 +2,10 @@ ( () << 0 @@ -65,16 +65,16 @@ assert ( importA 0 - 0 ^ 0 # +@@ -24,35 +14,15 @@ - --class A: -- def foo(self): + class A: + def foo(self): - for _ in range(10): - aaaaaaaaaaaaaaaaaaa = bbbbbbbbbbbbbbb.cccccccccc( - xxxxxxxxxxxx - ) # pylint: disable=no-member -+NOT_YET_IMPLEMENTED_StmtClassDef ++ for _ in NOT_IMPLEMENTED_call(): ++ aaaaaaaaaaaaaaaaaaa = NOT_IMPLEMENTED_call() def test(self, othr): @@ -126,7 +126,10 @@ importA 0 ^ 0 # -NOT_YET_IMPLEMENTED_StmtClassDef +class A: + def foo(self): + for _ in NOT_IMPLEMENTED_call(): + aaaaaaaaaaaaaaaaaaa = NOT_IMPLEMENTED_call() def test(self, othr): 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 9b6fe74f54d7be..1b0c59a2a92c66 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,34 +1,12 @@ +@@ -1,34 +1,20 @@ -if e1234123412341234.winerror not in ( - _winapi.ERROR_SEM_TIMEOUT, - _winapi.ERROR_PIPE_BUSY, @@ -58,25 +58,28 @@ class A: + new_id = NOT_IMPLEMENTED_call() + 1 --class X: -- def get_help_text(self): + class X: + def get_help_text(self): - 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} -+NOT_YET_IMPLEMENTED_StmtClassDef ++ return NOT_IMPLEMENTED_call() % {"NOT_YET_IMPLEMENTED_STRING": self.min_length} --class A: -- def b(self): + class A: + def b(self): - if self.connection.mysql_is_mariadb and ( - 10, - 4, - 3, - ) < self.connection.mysql_version < (10, 5, 2): -- pass -+NOT_YET_IMPLEMENTED_StmtClassDef ++ if ( ++ self.connection.mysql_is_mariadb ++ and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right ++ ): + pass ``` ## Ruff Output @@ -90,10 +93,18 @@ if x: new_id = NOT_IMPLEMENTED_call() + 1 -NOT_YET_IMPLEMENTED_StmtClassDef +class X: + def get_help_text(self): + return NOT_IMPLEMENTED_call() % {"NOT_YET_IMPLEMENTED_STRING": self.min_length} -NOT_YET_IMPLEMENTED_StmtClassDef +class A: + def b(self): + if ( + self.connection.mysql_is_mariadb + and NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right + ): + pass ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__ruff_test__statement__class_definition_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__ruff_test__statement__class_definition_py.snap new file mode 100644 index 00000000000000..84ba8cf766da63 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__ruff_test__statement__class_definition_py.snap @@ -0,0 +1,101 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +--- +## Input +```py +class Test( + Aaaaaaaaaaaaaaaaa, + Bbbbbbbbbbbbbbbb, + DDDDDDDDDDDDDDDD, + EEEEEEEEEEEEEE, + metaclass=meta, +): + pass + + +class Test((Aaaaaaaaaaaaaaaaa), Bbbbbbbbbbbbbbbb, metaclass=meta): + pass + +class Test( # trailing class comment + Aaaaaaaaaaaaaaaaa, # trailing comment + + # in between comment + + Bbbbbbbbbbbbbbbb, + # another leading comment + DDDDDDDDDDDDDDDD, + EEEEEEEEEEEEEE, + # meta comment + metaclass=meta, # trailing meta comment +): + pass + +class Test((Aaaa)): + ... + + +class Test(aaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbb + cccccccccccccccccccccccc + dddddddddddddddddddddd + eeeeeeeee, ffffffffffffffffff, gggggggggggggggggg): + pass + +class Test(Aaaa): # trailing comment + pass +``` + + + +## Output +```py +class Test( + Aaaaaaaaaaaaaaaaa, + Bbbbbbbbbbbbbbbb, + DDDDDDDDDDDDDDDD, + EEEEEEEEEEEEEE, + metaclass=meta, +): + pass + + +class Test( + (Aaaaaaaaaaaaaaaaa), + Bbbbbbbbbbbbbbbb, + metaclass=meta, +): + pass + + +class Test( + # trailing class comment + Aaaaaaaaaaaaaaaaa, # trailing comment + # in between comment + Bbbbbbbbbbbbbbbb, + # another leading comment + DDDDDDDDDDDDDDDD, + EEEEEEEEEEEEEE, + # meta comment + metaclass=meta, # trailing meta comment +): + pass + + +class Test((Aaaa)): + ... + + +class Test( + aaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbb + + cccccccccccccccccccccccc + + dddddddddddddddddddddd + + eeeeeeeee, + ffffffffffffffffff, + gggggggggggggggggg, +): + pass + + +class Test(Aaaa): # trailing comment + pass +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__ruff_test__trivia_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__ruff_test__trivia_py.snap index a103d8ccaee40b..df8702bcb0cd67 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__ruff_test__trivia_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__ruff_test__trivia_py.snap @@ -52,7 +52,10 @@ b = 20 # Adds two lines after `b` -NOT_YET_IMPLEMENTED_StmtClassDef +class Test: + def a(self): + pass + # trailing comment # two lines before, one line after diff --git a/crates/ruff_python_formatter/src/statement/stmt_class_def.rs b/crates/ruff_python_formatter/src/statement/stmt_class_def.rs index 5c2f8db3ab2f16..0c6427e9937f52 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_class_def.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_class_def.rs @@ -1,12 +1,133 @@ -use crate::{not_yet_implemented, FormatNodeRule, PyFormatter}; -use ruff_formatter::{write, Buffer, FormatResult}; -use rustpython_parser::ast::StmtClassDef; +use crate::comments::trailing_comments; +use crate::expression::parentheses::Parenthesize; +use crate::prelude::*; +use crate::trivia::{first_non_trivia_token, SimpleTokenizer, Token, TokenKind}; +use crate::USE_MAGIC_TRAILING_COMMA; +use ruff_formatter::{format_args, write}; +use ruff_text_size::TextRange; +use rustpython_parser::ast::{Expr, Keyword, Ranged, StmtClassDef}; #[derive(Default)] pub struct FormatStmtClassDef; impl FormatNodeRule for FormatStmtClassDef { fn fmt_fields(&self, item: &StmtClassDef, f: &mut PyFormatter) -> FormatResult<()> { - write!(f, [not_yet_implemented(item)]) + let StmtClassDef { + range: _, + name, + bases, + keywords, + body, + decorator_list, + } = item; + + f.join_with(hard_line_break()) + .entries(decorator_list.iter().formatted()) + .finish()?; + + if !decorator_list.is_empty() { + hard_line_break().fmt(f)?; + } + + write!(f, [text("class"), space(), name.format()])?; + + if !(bases.is_empty() && keywords.is_empty()) { + write!( + f, + [group(&format_args![ + text("("), + soft_block_indent(&FormatInheritanceClause { + class_definition: item + }), + text(")") + ])] + )?; + } + + let comments = f.context().comments().clone(); + let trailing_head_comments = comments.dangling_comments(item); + + write!( + f, + [ + text(":"), + trailing_comments(trailing_head_comments), + block_indent(&body.format()) + ] + ) + } + + fn fmt_dangling_comments( + &self, + _node: &StmtClassDef, + _f: &mut PyFormatter, + ) -> FormatResult<()> { + // handled in fmt_fields + Ok(()) + } +} + +struct FormatInheritanceClause<'a> { + class_definition: &'a StmtClassDef, +} + +impl Format> for FormatInheritanceClause<'_> { + fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { + let StmtClassDef { + bases, + keywords, + name, + .. + } = self.class_definition; + + let separator = format_with(|f| write!(f, [text(","), soft_line_break_or_space()])); + let source = f.context().contents(); + + let mut joiner = f.join_with(&separator); + + if let [first, rest @ ..] = bases.as_slice() { + // Manually handle parentheses here because the expression is parenthesized incorrectly assumes that the + // parentheses from the inheritance clause belong to the expression. + let tokenizer = SimpleTokenizer::new(source, TextRange::new(name.end(), first.start())) + .skip_trivia(); + + let left_paren_count = tokenizer + .take_while(|token| token.kind() == TokenKind::LParen) + .count(); + + // Ignore the first parentheses count + let parenthesize = if left_paren_count > 1 { + Parenthesize::Always + } else { + Parenthesize::Never + }; + + joiner.entry(&first.format().with_options(parenthesize)); + joiner.entries(rest.iter().formatted()); + } + + joiner.entries(keywords.iter().formatted()).finish()?; + + if_group_breaks(&text(",")).fmt(f)?; + + if USE_MAGIC_TRAILING_COMMA { + let last_end = bases + .last() + .map(Expr::end) + .or_else(|| keywords.last().map(Keyword::end)) + .unwrap(); + + if matches!( + first_non_trivia_token(last_end, f.context().contents()), + Some(Token { + kind: TokenKind::Comma, + .. + }) + ) { + hard_line_break().fmt(f)?; + } + } + + Ok(()) } } diff --git a/crates/ruff_python_formatter/src/statement/suite.rs b/crates/ruff_python_formatter/src/statement/suite.rs index 0a264a6214a72c..85329fba8ca62b 100644 --- a/crates/ruff_python_formatter/src/statement/suite.rs +++ b/crates/ruff_python_formatter/src/statement/suite.rs @@ -252,7 +252,8 @@ one_leading_newline = 10 no_leading_newline = 30 -NOT_YET_IMPLEMENTED_StmtClassDef +class InTheMiddle: + pass trailing_statement = 1 @@ -283,7 +284,8 @@ two_leading_newlines = 20 one_leading_newline = 10 no_leading_newline = 30 -NOT_YET_IMPLEMENTED_StmtClassDef +class InTheMiddle: + pass trailing_statement = 1