Leading, Dangling, and Trailing comments formatting (#4785)

This commit is contained in:
Micha Reiser 2023-06-02 09:26:36 +02:00 committed by GitHub
parent b2498c576f
commit 5d939222db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 824 additions and 771 deletions

View file

@ -1,4 +1,4 @@
use ruff_text_size::TextRange; use ruff_text_size::{TextRange, TextSize};
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
/// The source code of a document that gets formatted /// The source code of a document that gets formatted
@ -68,9 +68,17 @@ impl SourceCodeSlice {
&code.text[self.range] &code.text[self.range]
} }
pub fn range(&self) -> TextRange { pub const fn range(&self) -> TextRange {
self.range self.range
} }
pub const fn start(&self) -> TextSize {
self.range.start()
}
pub const fn end(&self) -> TextSize {
self.range.end()
}
} }
impl Debug for SourceCodeSlice { impl Debug for SourceCodeSlice {

View file

@ -0,0 +1,180 @@
use crate::context::NodeLevel;
use crate::prelude::*;
use crate::trivia::{lines_after, lines_before};
use ruff_formatter::{format_args, write};
use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::prelude::AstNode;
/// Formats the leading comments of a node.
pub(crate) fn leading_comments<T>(node: &T) -> FormatLeadingComments
where
T: AstNode,
{
FormatLeadingComments {
node: node.as_any_node_ref(),
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct FormatLeadingComments<'a> {
node: AnyNodeRef<'a>,
}
impl Format<PyFormatContext<'_>> for FormatLeadingComments<'_> {
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
let comments = f.context().comments().clone();
for comment in comments.leading_comments(self.node) {
let slice = comment.slice();
let lines_after_comment = lines_after(f.context().contents(), slice.end());
write!(
f,
[
source_text_slice(slice.range(), ContainsNewlines::No),
empty_lines(lines_after_comment)
]
)?;
comment.mark_formatted();
}
Ok(())
}
}
/// Formats the trailing comments of `node`
pub(crate) fn trailing_comments<T>(node: &T) -> FormatTrailingComments
where
T: AstNode,
{
FormatTrailingComments {
node: node.as_any_node_ref(),
}
}
pub(crate) struct FormatTrailingComments<'a> {
node: AnyNodeRef<'a>,
}
impl Format<PyFormatContext<'_>> for FormatTrailingComments<'_> {
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
let comments = f.context().comments().clone();
let mut has_empty_lines_before = false;
for trailing in comments.trailing_comments(self.node) {
let slice = trailing.slice();
let content = source_text_slice(slice.range(), ContainsNewlines::No);
let lines_before_comment = lines_before(f.context().contents(), slice.start());
has_empty_lines_before |= lines_before_comment > 0;
if has_empty_lines_before {
// A trailing comment at the end of a body or list
// ```python
// def test():
// pass
//
// # Some comment
// ```
write!(
f,
[
line_suffix(&format_with(|f| {
write!(f, [empty_lines(lines_before_comment), content])
})),
expand_parent()
]
)?;
} else {
write!(
f,
[
line_suffix(&format_args![space(), space(), content]),
expand_parent()
]
)?;
}
trailing.mark_formatted();
}
Ok(())
}
}
/// Formats the dangling comments of `node`.
pub(crate) fn dangling_comments<T>(node: &T) -> FormatDanglingComments
where
T: AstNode,
{
FormatDanglingComments {
node: node.as_any_node_ref(),
}
}
pub(crate) struct FormatDanglingComments<'a> {
node: AnyNodeRef<'a>,
}
impl Format<PyFormatContext<'_>> for FormatDanglingComments<'_> {
fn fmt(&self, f: &mut Formatter<PyFormatContext>) -> FormatResult<()> {
let comments = f.context().comments().clone();
let dangling_comments = comments.dangling_comments(self.node);
let mut first = true;
for comment in dangling_comments {
if first && comment.position().is_end_of_line() {
write!(f, [space(), space()])?;
}
write!(
f,
[
source_text_slice(comment.slice().range(), ContainsNewlines::No),
empty_lines(lines_after(f.context().contents(), comment.slice().end()))
]
)?;
comment.mark_formatted();
first = false;
}
Ok(())
}
}
// Helper that inserts the appropriate number of empty lines before a comment, depending on the node level.
// Top level: Up to two empty lines
// parenthesized: A single empty line
// other: Up to a single empty line
const fn empty_lines(lines: u32) -> FormatEmptyLines {
FormatEmptyLines { lines }
}
#[derive(Copy, Clone, Debug)]
struct FormatEmptyLines {
lines: u32,
}
impl Format<PyFormatContext<'_>> for FormatEmptyLines {
fn fmt(&self, f: &mut Formatter<PyFormatContext>) -> FormatResult<()> {
match f.context().node_level() {
NodeLevel::TopLevel => match self.lines {
0 | 1 => write!(f, [hard_line_break()]),
2 => write!(f, [empty_line()]),
_ => write!(f, [empty_line(), empty_line()]),
},
NodeLevel::Statement => match self.lines {
0 | 1 => write!(f, [hard_line_break()]),
_ => write!(f, [empty_line()]),
},
// Remove all whitespace in parenthesized expressions
NodeLevel::Parenthesized => write!(f, [hard_line_break()]),
}
}
}

View file

@ -93,6 +93,7 @@ use std::fmt::Debug;
use std::rc::Rc; use std::rc::Rc;
mod debug; mod debug;
mod format;
mod map; mod map;
mod node_key; mod node_key;
mod placement; mod placement;
@ -102,6 +103,7 @@ use crate::comments::debug::{DebugComment, DebugComments};
use crate::comments::map::MultiMap; use crate::comments::map::MultiMap;
use crate::comments::node_key::NodeRefEqualityKey; use crate::comments::node_key::NodeRefEqualityKey;
use crate::comments::visitor::CommentsVisitor; use crate::comments::visitor::CommentsVisitor;
pub(crate) use format::{dangling_comments, leading_comments, trailing_comments};
use ruff_formatter::{SourceCode, SourceCodeSlice}; use ruff_formatter::{SourceCode, SourceCodeSlice};
use ruff_python_ast::node::AnyNodeRef; use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::source_code::CommentRanges; use ruff_python_ast::source_code::CommentRanges;

View file

@ -8,6 +8,7 @@ pub struct PyFormatContext<'a> {
options: SimpleFormatOptions, options: SimpleFormatOptions,
contents: &'a str, contents: &'a str,
comments: Comments<'a>, comments: Comments<'a>,
node_level: NodeLevel,
} }
impl<'a> PyFormatContext<'a> { impl<'a> PyFormatContext<'a> {
@ -20,6 +21,7 @@ impl<'a> PyFormatContext<'a> {
options, options,
contents, contents,
comments, comments,
node_level: NodeLevel::TopLevel,
} }
} }
@ -33,6 +35,15 @@ impl<'a> PyFormatContext<'a> {
Locator::new(self.contents) Locator::new(self.contents)
} }
#[allow(unused)]
pub(crate) fn set_node_level(&mut self, level: NodeLevel) {
self.node_level = level;
}
pub(crate) fn node_level(&self) -> NodeLevel {
self.node_level
}
#[allow(unused)] #[allow(unused)]
pub(crate) fn comments(&self) -> &Comments<'a> { pub(crate) fn comments(&self) -> &Comments<'a> {
&self.comments &self.comments
@ -56,7 +67,24 @@ impl Debug for PyFormatContext<'_> {
f.debug_struct("PyFormatContext") f.debug_struct("PyFormatContext")
.field("options", &self.options) .field("options", &self.options)
.field("comments", &self.comments.debug(self.source_code())) .field("comments", &self.comments.debug(self.source_code()))
.field("node_level", &self.node_level)
.field("source", &self.contents) .field("source", &self.contents)
.finish() .finish()
} }
} }
/// What's the enclosing level of the outer node.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
pub(crate) enum NodeLevel {
/// Formatting statements on the module level.
#[default]
TopLevel,
/// Formatting nodes that are enclosed by a statement.
#[allow(unused)]
Statement,
/// Formatting nodes that are enclosed in a parenthesized expression.
#[allow(unused)]
Parenthesized,
}

View file

@ -14,7 +14,7 @@ use ruff_formatter::{
use ruff_python_ast::node::AstNode; use ruff_python_ast::node::AstNode;
use ruff_python_ast::source_code::{CommentRanges, CommentRangesBuilder, Locator}; use ruff_python_ast::source_code::{CommentRanges, CommentRangesBuilder, Locator};
use crate::comments::Comments; use crate::comments::{dangling_comments, leading_comments, trailing_comments, Comments};
use crate::context::PyFormatContext; use crate::context::PyFormatContext;
pub mod cli; pub mod cli;
@ -57,31 +57,31 @@ where
/// Formats the node's fields. /// Formats the node's fields.
fn fmt_fields(&self, item: &N, f: &mut PyFormatter) -> FormatResult<()>; fn fmt_fields(&self, item: &N, f: &mut PyFormatter) -> FormatResult<()>;
/// Formats the [leading comments](crate::comments#leading-comments) of the node. /// Formats the [leading comments](comments#leading-comments) of the node.
/// ///
/// You may want to override this method if you want to manually handle the formatting of comments /// You may want to override this method if you want to manually handle the formatting of comments
/// inside of the `fmt_fields` method or customize the formatting of the leading comments. /// inside of the `fmt_fields` method or customize the formatting of the leading comments.
fn fmt_leading_comments(&self, _node: &N, _f: &mut PyFormatter) -> FormatResult<()> { fn fmt_leading_comments(&self, node: &N, f: &mut PyFormatter) -> FormatResult<()> {
Ok(()) leading_comments(node).fmt(f)
} }
/// Formats the [dangling comments](rome_formatter::comments#dangling-comments) of the node. /// Formats the [dangling comments](comments#dangling-comments) of the node.
/// ///
/// You should override this method if the node handled by this rule can have dangling comments because the /// You should override this method if the node handled by this rule can have dangling comments because the
/// default implementation formats the dangling comments at the end of the node, which isn't ideal but ensures that /// default implementation formats the dangling comments at the end of the node, which isn't ideal but ensures that
/// no comments are dropped. /// no comments are dropped.
/// ///
/// A node can have dangling comments if all its children are tokens or if all node childrens are optional. /// A node can have dangling comments if all its children are tokens or if all node childrens are optional.
fn fmt_dangling_comments(&self, _node: &N, _f: &mut PyFormatter) -> FormatResult<()> { fn fmt_dangling_comments(&self, node: &N, f: &mut PyFormatter) -> FormatResult<()> {
Ok(()) dangling_comments(node).fmt(f)
} }
/// Formats the [trailing comments](rome_formatter::comments#trailing-comments) of the node. /// Formats the [trailing comments](comments#trailing-comments) of the node.
/// ///
/// You may want to override this method if you want to manually handle the formatting of comments /// You may want to override this method if you want to manually handle the formatting of comments
/// inside of the `fmt_fields` method or customize the formatting of the trailing comments. /// inside of the `fmt_fields` method or customize the formatting of the trailing comments.
fn fmt_trailing_comments(&self, _node: &N, _f: &mut PyFormatter) -> FormatResult<()> { fn fmt_trailing_comments(&self, node: &N, f: &mut PyFormatter) -> FormatResult<()> {
Ok(()) trailing_comments(node).fmt(f)
} }
} }
@ -180,8 +180,10 @@ if True:
print( "hi" ) print( "hi" )
# trailing # trailing
"#; "#;
let expected = r#"if True: let expected = r#"# preceding
if True:
print( "hi" ) print( "hi" )
# trailing
"#; "#;
let actual = format_module(input)?.as_code().to_string(); let actual = format_module(input)?.as_code().to_string();
assert_eq!(expected, actual); assert_eq!(expected, actual);

View file

@ -84,23 +84,23 @@ if True:
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,99 +1,57 @@ @@ -1,99 +1,61 @@
import core, time, a import core, time, a
- -
from . import A, B, C from . import A, B, C
- -
-# keeps existing trailing comma # keeps existing trailing comma
from foo import ( from foo import (
bar, bar,
) )
- -
-# also keeps existing structure # also keeps existing structure
from foo import ( from foo import (
baz, baz,
qux, qux,
) )
- -
-# `as` works as well # `as` works as well
from foo import ( from foo import (
xyzzy as magic, xyzzy as magic,
) )
@ -155,9 +155,9 @@ if True:
- % bar - % bar
-) -)
- -
-# looping over a 1-tuple should also not get wrapped
+y = {"oneple": (1,),} +y = {"oneple": (1,),}
+assert False, ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wraps %s" % bar) +assert False, ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wraps %s" % bar)
# looping over a 1-tuple should also not get wrapped
for x in (1,): for x in (1,):
pass pass
for (x,) in (1,), (2,), (3,): for (x,) in (1,), (2,), (3,):
@ -211,13 +211,16 @@ if True:
```py ```py
import core, time, a import core, time, a
from . import A, B, C from . import A, B, C
# keeps existing trailing comma
from foo import ( from foo import (
bar, bar,
) )
# also keeps existing structure
from foo import ( from foo import (
baz, baz,
qux, qux,
) )
# `as` works as well
from foo import ( from foo import (
xyzzy as magic, xyzzy as magic,
) )
@ -241,6 +244,7 @@ nested_long_lines = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbb
x = {"oneple": (1,)} x = {"oneple": (1,)}
y = {"oneple": (1,),} y = {"oneple": (1,),}
assert False, ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wraps %s" % bar) assert False, ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wraps %s" % bar)
# looping over a 1-tuple should also not get wrapped
for x in (1,): for x in (1,):
pass pass
for (x,) in (1,), (2,), (3,): for (x,) in (1,), (2,), (3,):

View file

@ -178,7 +178,7 @@ instruction()#comment with bad spacing
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,39 +1,36 @@ @@ -1,39 +1,38 @@
from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import (
- MyLovelyCompanyTeamProjectComponent, # NOT DRY - MyLovelyCompanyTeamProjectComponent, # NOT DRY
+ MyLovelyCompanyTeamProjectComponent # NOT DRY + MyLovelyCompanyTeamProjectComponent # NOT DRY
@ -188,8 +188,8 @@ 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__ = [ __all__ = [
# Super-special typing primitives. # Super-special typing primitives.
- "Any", - "Any",
@ -238,7 +238,7 @@ instruction()#comment with bad spacing
# builtin types and objects # builtin types and objects
type, type,
object, object,
@@ -47,21 +44,20 @@ @@ -47,21 +46,21 @@
Cheese("Wensleydale"), Cheese("Wensleydale"),
SubBytes(b"spam"), SubBytes(b"spam"),
] ]
@ -254,7 +254,7 @@ instruction()#comment with bad spacing
- # 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(): def inline_comments_in_brackets_ruin_everything():
if typedargslist: if typedargslist:
- parameters.children = [children[0], body, children[-1]] # (1 # )1 - parameters.children = [children[0], body, children[-1]] # (1 # )1
@ -267,7 +267,7 @@ instruction()#comment with bad spacing
children[0], children[0],
body, body,
children[-1], # type: ignore children[-1], # type: ignore
@@ -73,49 +69,42 @@ @@ -73,49 +72,42 @@
parameters.children[-1], # )2 parameters.children[-1], # )2
] ]
parameters.children = [parameters.what_if_this_was_actually_long.children[0], body, parameters.children[-1]] # type: ignore parameters.children = [parameters.what_if_this_was_actually_long.children[0], body, parameters.children[-1]] # type: ignore
@ -340,7 +340,7 @@ instruction()#comment with bad spacing
] ]
lcomp2 = [ lcomp2 = [
# hello # hello
@@ -127,7 +116,7 @@ @@ -127,7 +119,7 @@
] ]
lcomp3 = [ lcomp3 = [
# This one is actually too long to fit in a single line. # This one is actually too long to fit in a single line.
@ -349,7 +349,7 @@ instruction()#comment with bad spacing
# yup # yup
for element in collection.select_elements() for element in collection.select_elements()
# right # right
@@ -140,34 +129,18 @@ @@ -140,34 +132,26 @@
# and round and round we go # and round and round we go
# and round and round we go # and round and round we go
@ -374,7 +374,7 @@ instruction()#comment with bad spacing
-) # type: Final -) # type: Final
- -
- -
+CONFIG_FILES = [CONFIG_FILE, ] + SHARED_CONFIG_FILES + USER_CONFIG_FILES +CONFIG_FILES = [CONFIG_FILE, ] + SHARED_CONFIG_FILES + USER_CONFIG_FILES # type: Final
class Test: class Test:
def _init_host(self, parsed) -> None: def _init_host(self, parsed) -> None:
- if parsed.hostname is None or not parsed.hostname.strip(): # type: ignore - if parsed.hostname is None or not parsed.hostname.strip(): # type: ignore
@ -383,16 +383,16 @@ instruction()#comment with bad spacing
pass pass
- -
- -
-####################### #######################
-### SECTION COMMENT ### ### SECTION COMMENT ###
-####################### #######################
-
-
-instruction() # comment with bad spacing -instruction() # comment with bad spacing
- +instruction() #comment with bad spacing
-# END COMMENTS
-# MORE END COMMENTS # END COMMENTS
+instruction() # MORE END COMMENTS
``` ```
## Ruff Output ## Ruff Output
@ -404,6 +404,8 @@ 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 ( from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import (
MyLovelyCompanyTeamProjectComponent as component # DRY MyLovelyCompanyTeamProjectComponent as component # DRY
) )
# Please keep __all__ alphabetized within each category.
__all__ = [ __all__ = [
# Super-special typing primitives. # Super-special typing primitives.
'Any', 'Any',
@ -450,6 +452,7 @@ else:
# for compiler in compilers.values(): # for compiler in compilers.values():
# add_compiler(compiler) # add_compiler(compiler)
add_compiler(compilers[(7.0, 32)]) add_compiler(compilers[(7.0, 32)])
# Comment before function.
def inline_comments_in_brackets_ruin_everything(): def inline_comments_in_brackets_ruin_everything():
if typedargslist: if typedargslist:
parameters.children = [ parameters.children = [
@ -537,13 +540,21 @@ short
Leaf(token.NEWLINE, '\n') # FIXME: \r\n? Leaf(token.NEWLINE, '\n') # FIXME: \r\n?
], ],
) )
CONFIG_FILES = [CONFIG_FILE, ] + SHARED_CONFIG_FILES + USER_CONFIG_FILES CONFIG_FILES = [CONFIG_FILE, ] + SHARED_CONFIG_FILES + USER_CONFIG_FILES # type: Final
class Test: class Test:
def _init_host(self, parsed) -> None: def _init_host(self, parsed) -> None:
if (parsed.hostname is None or # type: ignore if (parsed.hostname is None or # type: ignore
not parsed.hostname.strip()): not parsed.hostname.strip()):
pass pass
instruction() #######################
### SECTION COMMENT ###
#######################
instruction() #comment with bad spacing
# END COMMENTS
# MORE END COMMENTS
``` ```
## Black Output ## Black Output

View file

@ -1,180 +0,0 @@
---
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),
)
# %%
```

View file

@ -86,22 +86,19 @@ if __name__ == "__main__":
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,13 +1,6 @@ @@ -1,11 +1,6 @@
while True: while True:
if something.changed: if something.changed:
- do.stuff() # trailing comment do.stuff() # trailing comment
- # Comment belongs to the `if` block. - # Comment belongs to the `if` block.
- # This one belongs to the `while` block. - # This one belongs to the `while` block.
- -
- # Should this one, too? I guess so. - # Should this one, too? I guess so.
- -
-# This one is properly standalone now. # This one is properly standalone now.
-
+ do.stuff()
for i in range(100): for i in range(100):
# first we do this @@ -15,27 +10,18 @@
if i % 33 == 0:
@@ -15,31 +8,17 @@
# then we do this # then we do this
print(i) print(i)
@ -120,41 +117,37 @@ if __name__ == "__main__":
import sys import sys
- -
- -
-# leading function comment # leading function comment
def wat(): def wat():
... ...
- # trailing function comment - # trailing function comment
- -
- -
-# SECTION COMMENT # SECTION COMMENT
-
-
-# leading 1 @@ -47,8 +33,6 @@
@deco1
# leading 2
@deco2(with_args=True)
@@ -47,27 +26,14 @@
@deco3 @deco3
def decorated1(): def decorated1():
... ...
- -
- -
-# leading 1 # leading 1
@deco1 @deco1
# leading 2 # leading 2
@deco2(with_args=True) @@ -56,18 +40,12 @@
# leading function comment # leading function comment
def decorated1(): def decorated1():
... ...
- -
- -
-# Note: this is fixed in # Note: this is fixed in
-# Preview.empty_lines_before_class_or_def_with_leading_comments. # Preview.empty_lines_before_class_or_def_with_leading_comments.
-# In the current style, the user will have to split those lines by hand. # In the current style, the user will have to split those lines by hand.
some_instruction some_instruction
- -
- -
-# This comment should be split from `some_instruction` by two lines but isn't. # This comment should be split from `some_instruction` by two lines but isn't.
def g(): def g():
... ...
- -
@ -168,7 +161,9 @@ if __name__ == "__main__":
```py ```py
while True: while True:
if something.changed: if something.changed:
do.stuff() do.stuff() # trailing comment
# This one is properly standalone now.
for i in range(100): for i in range(100):
# first we do this # first we do this
if i % 33 == 0: if i % 33 == 0:
@ -185,8 +180,13 @@ try:
except OSError: except OSError:
print("problems") print("problems")
import sys import sys
# leading function comment
def wat(): def wat():
... ...
# SECTION COMMENT
# leading 1
@deco1 @deco1
# leading 2 # leading 2
@deco2(with_args=True) @deco2(with_args=True)
@ -194,13 +194,18 @@ def wat():
@deco3 @deco3
def decorated1(): def decorated1():
... ...
# leading 1
@deco1 @deco1
# leading 2 # leading 2
@deco2(with_args=True) @deco2(with_args=True)
# leading function comment # leading function comment
def decorated1(): 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 some_instruction
# This comment should be split from `some_instruction` by two lines but isn't.
def g(): def g():
... ...
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -131,7 +131,7 @@ aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*ite
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,18 +1,11 @@ @@ -1,18 +1,12 @@
from typing import Any, Tuple from typing import Any, Tuple
- -
- -
@ -141,7 +141,7 @@ aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*ite
pass pass
- -
- -
-# test type comments # test type comments
def f(a, b, c, d, e, f, g, h, i): def f(a, b, c, d, e, f, g, h, i):
# type: (int, int, int, int, int, int, int, int, int) -> None # type: (int, int, int, int, int, int, int, int, int) -> None
pass pass
@ -150,7 +150,7 @@ aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*ite
def f( def f(
a, # type: int a, # type: int
b, # type: int b, # type: int
@@ -26,8 +19,6 @@ @@ -26,8 +20,6 @@
): ):
# type: (...) -> None # type: (...) -> None
pass pass
@ -159,7 +159,7 @@ aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*ite
def f( def f(
arg, # type: int arg, # type: int
*args, # type: *Any *args, # type: *Any
@@ -36,8 +27,6 @@ @@ -36,8 +28,6 @@
): ):
# type: (...) -> None # type: (...) -> None
pass pass
@ -168,14 +168,12 @@ aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*ite
def f( def f(
a, # type: int a, # type: int
b, # type: int b, # type: int
@@ -66,23 +55,17 @@ @@ -67,22 +57,16 @@
+ element
+ another_element + another_element
+ another_element_with_long_name + another_element_with_long_name
- ) # type: int ) # type: int
- -
- -
+ )
def f( def f(
x, # not a type comment x, # not a type comment
y, # type: int y, # type: int
@ -193,7 +191,7 @@ aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*ite
def func( def func(
a=some_list[0], # type: int a=some_list[0], # type: int
): # type: () -> int ): # type: () -> int
@@ -102,17 +85,12 @@ @@ -102,17 +86,12 @@
c = call( c = call(
"aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa" # type: ignore "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa" # type: ignore
) )
@ -203,16 +201,14 @@ aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*ite
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
) )
- -
-AAAAAAAAAAAAA = [AAAAAAAAAAAAA] + SHARED_AAAAAAAAAAAAA + USER_AAAAAAAAAAAAA + AAAAAAAAAAAAA # type: ignore AAAAAAAAAAAAA = [AAAAAAAAAAAAA] + SHARED_AAAAAAAAAAAAA + USER_AAAAAAAAAAAAA + AAAAAAAAAAAAA # type: ignore
- -
+AAAAAAAAAAAAA = [AAAAAAAAAAAAA] + SHARED_AAAAAAAAAAAAA + USER_AAAAAAAAAAAAA + AAAAAAAAAAAAA
call_to_some_function_asdf( call_to_some_function_asdf(
foo, foo,
[AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, BBBBBBBBBBBB], # type: ignore [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))) # type: ignore[arg-type]
+aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items)))
``` ```
## Ruff Output ## Ruff Output
@ -223,6 +219,7 @@ def f(
a, # type: int a, # type: int
): ):
pass pass
# test type comments
def f(a, b, c, d, e, f, g, h, i): def f(a, b, c, d, e, f, g, h, i):
# type: (int, int, int, int, int, int, int, int, int) -> None # type: (int, int, int, int, int, int, int, int, int) -> None
pass pass
@ -275,7 +272,7 @@ def f(
+ element + element
+ another_element + another_element
+ another_element_with_long_name + another_element_with_long_name
) ) # type: int
def f( def f(
x, # not a type comment x, # not a type comment
y, # type: int y, # type: int
@ -308,12 +305,12 @@ def func(
result = ( # aaa result = ( # aaa
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
) )
AAAAAAAAAAAAA = [AAAAAAAAAAAAA] + SHARED_AAAAAAAAAAAAA + USER_AAAAAAAAAAAAA + AAAAAAAAAAAAA AAAAAAAAAAAAA = [AAAAAAAAAAAAA] + SHARED_AAAAAAAAAAAAA + USER_AAAAAAAAAAAAA + AAAAAAAAAAAAA # type: ignore
call_to_some_function_asdf( call_to_some_function_asdf(
foo, foo,
[AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, BBBBBBBBBBBB], # type: ignore [AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, BBBBBBBBBBBB], # type: ignore
) )
aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) # type: ignore[arg-type]
``` ```
## Black Output ## Black Output

View file

@ -19,18 +19,24 @@ input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_ca
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,6 +0,0 @@ @@ -2,5 +2,5 @@
-# The percent-percent comments are Spyder IDE cells. # Both `#%%`` and `# %%` are accepted, so `black` standardises
-# Both `#%%`` and `# %%` are accepted, so `black` standardises # to the latter.
-# to the latter.
-
-# %%
-# %% -# %%
+#%%
# %%
``` ```
## Ruff Output ## Ruff Output
```py ```py
# The percent-percent comments are Spyder IDE cells.
# Both `#%%`` and `# %%` are accepted, so `black` standardises
# to the latter.
#%%
# %%
``` ```
## Black Output ## Black Output

View file

@ -152,13 +152,13 @@ def bar():
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,61 +1,23 @@ @@ -1,60 +1,35 @@
-# Test for https://github.com/psf/black/issues/246. # Test for https://github.com/psf/black/issues/246.
-
some = statement some = statement
- -
- -
-# This comment should be split from the statement above by two lines. # This comment should be split from the statement above by two lines.
def function(): def function():
pass pass
- -
@ -166,9 +166,9 @@ def bar():
some = statement some = statement
- -
- -
-# This multiline comments section # This multiline comments section
-# should be split from the statement # should be split from the statement
-# above by two lines. # above by two lines.
def function(): def function():
pass pass
- -
@ -176,7 +176,7 @@ def bar():
some = statement some = statement
- -
- -
-# This comment should be split from the statement above by two lines. # This comment should be split from the statement above by two lines.
async def async_function(): async def async_function():
pass pass
- -
@ -184,16 +184,16 @@ def bar():
some = statement some = statement
- -
- -
-# This comment should be split from the statement above by two lines. # This comment should be split from the statement above by two lines.
class MyClass: class MyClass:
pass pass
- -
- -
some = statement some = statement
-# This should be stick to the statement above # This should be stick to the statement above
- -
- # This should be split from the above by two lines
-# This should be split from the above by two lines
class MyClassWithComplexLeadingComments: class MyClassWithComplexLeadingComments:
pass pass
- -
@ -202,7 +202,7 @@ def bar():
"""A docstring.""" """A docstring."""
- -
- -
-# Leading comment after a class with just a docstring # Leading comment after a class with just a docstring
class MyClassAfterAnotherClassWithDocstring: class MyClassAfterAnotherClassWithDocstring:
pass pass
- -
@ -210,11 +210,10 @@ def bar():
some = statement some = statement
- -
- -
-# leading 1 # leading 1
@deco1 @deco1
# leading 2 # leading 2
# leading 2 extra @@ -65,11 +40,7 @@
@@ -65,12 +27,7 @@
# leading 4 # leading 4
def decorated(): def decorated():
pass pass
@ -223,11 +222,10 @@ def bar():
some = statement some = statement
- -
- -
-# leading 1 # leading 1
@deco1 @deco1
# leading 2 # leading 2
@deco2(with_args=True) @@ -80,11 +51,7 @@
@@ -80,12 +37,7 @@
# leading 4 # leading 4
def decorated_with_split_leading_comments(): def decorated_with_split_leading_comments():
pass pass
@ -236,11 +234,10 @@ def bar():
some = statement some = statement
- -
- -
-# leading 1 # leading 1
@deco1 @deco1
# leading 2 # leading 2
@deco2(with_args=True) @@ -95,66 +62,44 @@
@@ -95,66 +47,42 @@
# leading 4 that already has an empty line # leading 4 that already has an empty line
def decorated_with_split_leading_comments(): def decorated_with_split_leading_comments():
pass pass
@ -284,7 +281,7 @@ def bar():
pass pass
- -
- -
-# Regression test for https://github.com/psf/black/issues/3454. # Regression test for https://github.com/psf/black/issues/3454.
def foo(): def foo():
pass pass
- # Trailing comment that belongs to this function - # Trailing comment that belongs to this function
@ -296,7 +293,7 @@ def bar():
pass pass
- -
- -
-# Regression test for https://github.com/psf/black/issues/3454. # Regression test for https://github.com/psf/black/issues/3454.
def foo(): def foo():
pass pass
- # Trailing comment that belongs to this function. - # Trailing comment that belongs to this function.
@ -312,26 +309,39 @@ def bar():
## Ruff Output ## Ruff Output
```py ```py
# Test for https://github.com/psf/black/issues/246.
some = statement some = statement
# This comment should be split from the statement above by two lines.
def function(): def function():
pass pass
some = statement some = statement
# This multiline comments section
# should be split from the statement
# above by two lines.
def function(): def function():
pass pass
some = statement some = statement
# This comment should be split from the statement above by two lines.
async def async_function(): async def async_function():
pass pass
some = statement some = statement
# This comment should be split from the statement above by two lines.
class MyClass: class MyClass:
pass pass
some = statement some = statement
# This should be stick to the statement above
# This should be split from the above by two lines
class MyClassWithComplexLeadingComments: class MyClassWithComplexLeadingComments:
pass pass
class ClassWithDocstring: class ClassWithDocstring:
"""A docstring.""" """A docstring."""
# Leading comment after a class with just a docstring
class MyClassAfterAnotherClassWithDocstring: class MyClassAfterAnotherClassWithDocstring:
pass pass
some = statement some = statement
# leading 1
@deco1 @deco1
# leading 2 # leading 2
# leading 2 extra # leading 2 extra
@ -342,6 +352,7 @@ some = statement
def decorated(): def decorated():
pass pass
some = statement some = statement
# leading 1
@deco1 @deco1
# leading 2 # leading 2
@deco2(with_args=True) @deco2(with_args=True)
@ -352,6 +363,7 @@ some = statement
def decorated_with_split_leading_comments(): def decorated_with_split_leading_comments():
pass pass
some = statement some = statement
# leading 1
@deco1 @deco1
# leading 2 # leading 2
@deco2(with_args=True) @deco2(with_args=True)
@ -389,12 +401,14 @@ class MyClass:
# More comments. # More comments.
def first_method(self): def first_method(self):
pass pass
# Regression test for https://github.com/psf/black/issues/3454.
def foo(): def foo():
pass pass
@decorator1 @decorator1
@decorator2 # fmt: skip @decorator2 # fmt: skip
def bar(): def bar():
pass pass
# Regression test for https://github.com/psf/black/issues/3454.
def foo(): def foo():
pass pass
@decorator1 @decorator1

View file

@ -54,11 +54,11 @@ def function(a:int=42):
- """This docstring is already formatted - """This docstring is already formatted
- a - a
- b - b
+result = 1 +result = 1 # A simple comment
+result = ( 1, ) +result = ( 1, ) # Another one
+result = 1 +result = 1 # type: ignore
+result = 1 +result = 1 # This comment is talking about type: ignore
+square = Square(4) +square = Square(4) # type: Optional[Square]
+def function(a:int=42): +def function(a:int=42):
+ """ This docstring is already formatted + """ This docstring is already formatted
+ a + a
@ -76,11 +76,11 @@ def function(a:int=42):
from .config import ( ConfigTypeAttributes, Int, Path, # String, from .config import ( ConfigTypeAttributes, Int, Path, # String,
# DEFAULT_TYPE_ATTRIBUTES, # DEFAULT_TYPE_ATTRIBUTES,
) )
result = 1 result = 1 # A simple comment
result = ( 1, ) result = ( 1, ) # Another one
result = 1 result = 1 # type: ignore
result = 1 result = 1 # This comment is talking about type: ignore
square = Square(4) square = Square(4) # type: Optional[Square]
def function(a:int=42): def function(a:int=42):
""" This docstring is already formatted """ This docstring is already formatted
a a

View file

@ -109,14 +109,7 @@ async def wat():
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,34 +1,20 @@ @@ -8,27 +8,19 @@
-#!/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. Possibly also many, many lines.
""" """
@ -125,9 +118,8 @@ async def wat():
import sys import sys
- -
import a import a
-from b.c import X # some noqa comment from b.c import X # some noqa comment
- -
+from b.c import X
try: try:
import fast import fast
except ImportError: except ImportError:
@ -145,50 +137,46 @@ async def wat():
def function(default=None): def function(default=None):
"""Docstring comes first. """Docstring comes first.
@@ -45,16 +31,7 @@ @@ -45,12 +37,8 @@
# This return is also commented for some reason. # This return is also commented for some reason.
return default return default
- -
- -
-# Explains why we use global state. # Explains why we use global state.
GLOBAL_STATE = {"a": a(1), "b": a(2), "c": a(3)} GLOBAL_STATE = {"a": a(1), "b": a(2), "c": a(3)}
- -
- -
-# Another comment! # Another comment!
-# This time two lines. # This time two lines.
-
-
class Foo:
"""Docstring for class Foo. Example from Sphinx docs."""
@@ -73,11 +50,6 @@ @@ -73,8 +61,6 @@
self.spam = 4 self.spam = 4
"""Docstring for instance attribute spam.""" """Docstring for instance attribute spam."""
- -
- -
-#' <h1>This is pweave!</h1> #' <h1>This is pweave!</h1>
-
-
@fast(really=True) @@ -93,4 +79,4 @@
async def wat():
# This comment, for some reason \ # Some closing comments.
@@ -89,8 +61,3 @@ # Maybe Vim or Emacs directives for formatting.
print("A OK", file=sys.stdout)
# Comment between things.
print()
-
-
-# Some closing comments.
-# Maybe Vim or Emacs directives for formatting.
-# Who knows. -# Who knows.
\ No newline at end of file \ No newline at end of file
+# Who knows.
``` ```
## Ruff Output ## Ruff Output
```py ```py
#!/usr/bin/env python3
# fmt: on
# Some license here.
#
# Has many lines. Many, many lines.
# Many, many, many lines.
"""Module docstring. """Module docstring.
Possibly also many, many lines. Possibly also many, many lines.
@ -196,7 +184,7 @@ Possibly also many, many lines.
import os.path import os.path
import sys import sys
import a import a
from b.c import X from b.c import X # some noqa comment
try: try:
import fast import fast
except ImportError: except ImportError:
@ -222,7 +210,12 @@ def function(default=None):
# This return is also commented for some reason. # This return is also commented for some reason.
return default return default
# Explains why we use global state.
GLOBAL_STATE = {"a": a(1), "b": a(2), "c": a(3)} GLOBAL_STATE = {"a": a(1), "b": a(2), "c": a(3)}
# Another comment!
# This time two lines.
class Foo: class Foo:
"""Docstring for class Foo. Example from Sphinx docs.""" """Docstring for class Foo. Example from Sphinx docs."""
@ -241,6 +234,9 @@ class Foo:
self.spam = 4 self.spam = 4
"""Docstring for instance attribute spam.""" """Docstring for instance attribute spam."""
#' <h1>This is pweave!</h1>
@fast(really=True) @fast(really=True)
async def wat(): async def wat():
# This comment, for some reason \ # This comment, for some reason \
@ -252,6 +248,11 @@ async def wat():
print("A OK", file=sys.stdout) print("A OK", file=sys.stdout)
# Comment between things. # Comment between things.
print() print()
# Some closing comments.
# Maybe Vim or Emacs directives for formatting.
# Who knows.
``` ```
## Black Output ## Black Output

View file

@ -1,43 +0,0 @@
---
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."""
```

View file

@ -234,7 +234,7 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self):
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,219 +1,155 @@ @@ -1,219 +1,157 @@
class MyClass: class MyClass:
+ """ Multiline + """ Multiline
+ class docstring + class docstring
@ -349,11 +349,11 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self):
+ - And the closing quote is too deep + - And the closing quote is too deep
""" """
- pass - pass
-
+ pass + pass
+def single_line(): +def single_line():
+ """But with a newline after it! + """But with a newline after it!
-
-def over_indent(): -def over_indent():
- """ - """
- This has a shallow indent - This has a shallow indent
@ -387,22 +387,22 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self):
+ """ + """
+ "hey yah" """ + "hey yah" """
def and_this(): def and_this():
- ''' + '''
+ "hey yah"'''
+def multiline_whitespace():
+ '''
+
+
+
+
'''
- "hey yah"''' - "hey yah"'''
- -
- -
+ ''' -def multiline_whitespace():
+ "hey yah"'''
def multiline_whitespace():
- """ """ - """ """
- -
- -
+ '''
+
+
+
+
+ '''
def oneline_whitespace(): def oneline_whitespace():
- """ """ - """ """
- -
@ -453,13 +453,13 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self):
- -
def docstring_with_inline_tabs_and_tab_indentation(): def docstring_with_inline_tabs_and_tab_indentation():
- """hey - """hey
- + """hey
- tab separated value - tab separated value
- tab at start of line and then a tab separated value - tab at start of line and then a tab separated value
- multiple tabs at the beginning and inline - multiple tabs at the beginning and inline
- mixed tabs and spaces at beginning. next line has mixed tabs and spaces only. - mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
+ """hey -
- line ends with some tabs - line ends with some tabs
- """ - """
- pass - pass
@ -490,9 +490,9 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self):
- hey there \ """ - hey there \ """
- -
- -
-# Regression test for #3425
+ ''' + '''
+ hey there \ ''' + hey there \ '''
# Regression test for #3425
def multiline_backslash_really_long_dont_crash(): def multiline_backslash_really_long_dont_crash():
""" """
hey there hello guten tag hi hoow are you ola zdravstvuyte ciao como estas ca va \ """ hey there hello guten tag hi hoow are you ola zdravstvuyte ciao como estas ca va \ """
@ -509,7 +509,7 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self):
"I'm sorry Dave\u2001" "I'm sorry Dave\u2001"
- -
- -
-# the space below is actually a \u2001, removed in output # the space below is actually a \u2001, removed in output
def my_god_its_full_of_stars_2(): def my_god_its_full_of_stars_2():
- "I'm sorry Dave" - "I'm sorry Dave"
- -
@ -675,6 +675,7 @@ def multiline_backslash_1():
def multiline_backslash_2(): def multiline_backslash_2():
''' '''
hey there \ ''' hey there \ '''
# Regression test for #3425
def multiline_backslash_really_long_dont_crash(): def multiline_backslash_really_long_dont_crash():
""" """
hey there hello guten tag hi hoow are you ola zdravstvuyte ciao como estas ca va \ """ hey there hello guten tag hi hoow are you ola zdravstvuyte ciao como estas ca va \ """
@ -683,6 +684,7 @@ def multiline_backslash_3():
already escaped \\ ''' already escaped \\ '''
def my_god_its_full_of_stars_1(): def my_god_its_full_of_stars_1():
"I'm sorry Dave\u2001" "I'm sorry Dave\u2001"
# the space below is actually a \u2001, removed in output
def my_god_its_full_of_stars_2(): def my_god_its_full_of_stars_2():
"I'm sorry Dave" "I'm sorry Dave"
def docstring_almost_at_line_limit(): def docstring_almost_at_line_limit():

View file

@ -105,11 +105,11 @@ def g():
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,11 +1,8 @@ @@ -1,11 +1,9 @@
"""Docstring.""" """Docstring."""
- -
- -
-# leading comment # leading comment
def f(): def f():
- NO = "" - NO = ""
- SPACE = " " - SPACE = " "
@ -120,7 +120,7 @@ def g():
t = leaf.type t = leaf.type
p = leaf.parent # trailing comment p = leaf.parent # trailing comment
@@ -16,14 +13,19 @@ @@ -16,14 +14,19 @@
if t == token.COMMENT: # another trailing comment if t == token.COMMENT: # another trailing comment
return DOUBLESPACE return DOUBLESPACE
@ -140,16 +140,16 @@ def g():
if prevp.type == token.EQUAL: if prevp.type == token.EQUAL:
if prevp.parent and prevp.parent.type in { if prevp.parent and prevp.parent.type in {
syms.typedargslist, syms.typedargslist,
@@ -43,17 +45,10 @@ @@ -43,17 +46,14 @@
syms.dictsetmaker, syms.dictsetmaker,
}: }:
return NO return NO
- -
- -
-############################################################################### ###############################################################################
-# SECTION BECAUSE SECTIONS # SECTION BECAUSE SECTIONS
-############################################################################### ###############################################################################
-
- -
def g(): def g():
- NO = "" - NO = ""
@ -161,7 +161,7 @@ def g():
t = leaf.type t = leaf.type
p = leaf.parent p = leaf.parent
@@ -67,7 +62,7 @@ @@ -67,7 +67,7 @@
return DOUBLESPACE return DOUBLESPACE
# Another comment because more comments # Another comment because more comments
@ -176,6 +176,7 @@ def g():
```py ```py
"""Docstring.""" """Docstring."""
# leading comment
def f(): def f():
NO = '' NO = ''
SPACE = ' ' SPACE = ' '
@ -222,6 +223,10 @@ def f():
syms.dictsetmaker, syms.dictsetmaker,
}: }:
return NO return NO
###############################################################################
# SECTION BECAUSE SECTIONS
###############################################################################
def g(): def g():
NO = '' NO = ''
SPACE = ' ' SPACE = ' '

View file

@ -401,7 +401,7 @@ last_call()
+call(kwarg='hey') +call(kwarg='hey')
+call(arg, kwarg='hey') +call(arg, kwarg='hey')
+call(arg, another, kwarg='hey', **kwargs) +call(arg, another, kwarg='hey', **kwargs)
+call(this_is_a_very_long_variable_which_will_force_a_delimiter_split, 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(*gidgets[:2]) call(*gidgets[:2])
call(a, *gidgets[:2]) call(a, *gidgets[:2])
call(**self.screen_kwargs) call(**self.screen_kwargs)
@ -437,7 +437,7 @@ last_call()
-) # type: ignore -) # type: ignore
+xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[ +xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[
+ ..., List[SomeClass] + ..., List[SomeClass]
+] = classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) +] = classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) # type: ignore
slice[0] slice[0]
slice[0:1] slice[0:1]
slice[0:1:2] slice[0:1:2]
@ -510,7 +510,7 @@ last_call()
Ø = set() Ø = set()
authors.łukasz.say_thanks() authors.łukasz.say_thanks()
mapping = { mapping = {
@@ -233,138 +170,82 @@ @@ -233,138 +170,83 @@
C: 0.1 * (10.0 / 12), C: 0.1 * (10.0 / 12),
D: 0.1 * (10.0 / 12), D: 0.1 * (10.0 / 12),
} }
@ -682,7 +682,7 @@ last_call()
-) -)
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ^bbbb.a & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ^bbbb.a & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
last_call() last_call()
-# standalone comment at ENDMARKER # standalone comment at ENDMARKER
``` ```
## Ruff Output ## Ruff Output
@ -773,7 +773,7 @@ call(arg)
call(kwarg='hey') call(kwarg='hey')
call(arg, kwarg='hey') call(arg, kwarg='hey')
call(arg, another, kwarg='hey', **kwargs) call(arg, another, kwarg='hey', **kwargs)
call(this_is_a_very_long_variable_which_will_force_a_delimiter_split, 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(*gidgets[:2]) call(*gidgets[:2])
call(a, *gidgets[:2]) call(a, *gidgets[:2])
call(**self.screen_kwargs) 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[ xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[
..., List[SomeClass] ..., List[SomeClass]
] = classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) ] = classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) # type: ignore
slice[0] slice[0]
slice[0:1] slice[0:1]
slice[0:1:2] slice[0:1:2]
@ -939,6 +939,7 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >> aaaaaaaaaaaaa
bbbb >> bbbb * bbbb bbbb >> bbbb * bbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ^bbbb.a & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ^bbbb.a & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
last_call() last_call()
# standalone comment at ENDMARKER
``` ```
## Black Output ## Black Output

View file

@ -53,21 +53,16 @@ def test_calculate_fades():
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,13 +1,6 @@ @@ -1,8 +1,6 @@
import pytest import pytest
- -
TmSt = 1 TmSt = 1
TmEx = 2 TmEx = 2
- -
-# fmt: off # fmt: off
-
-# Test data:
-# Position, Volume, State, TmSt/TmEx/None, [call, [arg1...]]
-
@pytest.mark.parametrize('test', [
# Test don't manage the volume # Test data:
@@ -17,24 +10,18 @@ @@ -17,19 +15,15 @@
]) ])
def test_fader(test): def test_fader(test):
pass pass
@ -87,11 +82,6 @@ def test_calculate_fades():
def test_calculate_fades(): def test_calculate_fades():
calcs = [ calcs = [
# one is zero/none # 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 ## Ruff Output
@ -100,6 +90,11 @@ def test_calculate_fades():
import pytest import pytest
TmSt = 1 TmSt = 1
TmEx = 2 TmEx = 2
# fmt: off
# Test data:
# Position, Volume, State, TmSt/TmEx/None, [call, [arg1...]]
@pytest.mark.parametrize('test', [ @pytest.mark.parametrize('test', [
# Test don't manage the volume # Test don't manage the volume
@ -124,6 +119,8 @@ def test_calculate_fades():
(0, 4, 0, 0, 10, 0, 0, 6, 10), (0, 4, 0, 0, 10, 0, 0, 6, 10),
(None, 4, 0, 0, 10, 0, 0, 6, 10), (None, 4, 0, 0, 10, 0, 0, 6, 10),
] ]
# fmt: on
``` ```
## Black Output ## Black Output

View file

@ -30,21 +30,10 @@ x = [
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,15 +1,11 @@ @@ -12,4 +12,6 @@
-# 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]
+x = [ +x = [
+ 1, 2, 3, 4 + 1, 2, 3, 4
@ -54,14 +43,20 @@ x = [
## Ruff Output ## Ruff Output
```py ```py
# fmt: off
x = [ x = [
1, 2, 1, 2,
3, 4, 3, 4,
] ]
# fmt: on
# fmt: off
x = [ x = [
1, 2, 1, 2,
3, 4, 3, 4,
] ]
# fmt: on
x = [ x = [
1, 2, 3, 4 1, 2, 3, 4
] ]

View file

@ -26,10 +26,7 @@ def f(): pass
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,20 +1,11 @@ @@ -4,17 +4,9 @@
-# fmt: off
@test([
1, 2,
3, 4, 3, 4,
]) ])
# fmt: on # fmt: on
@ -58,6 +55,7 @@ def f(): pass
## Ruff Output ## Ruff Output
```py ```py
# fmt: off
@test([ @test([
1, 2, 1, 2,
3, 4, 3, 4,

View file

@ -97,62 +97,53 @@ elif unformatted:
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,4 +1,3 @@ @@ -9,8 +9,6 @@
-# Regression test for https://github.com/psf/black/issues/3129.
setup(
entry_points={
# fmt: off
@@ -9,9 +8,6 @@
] # Includes an formatted indentation. ] # Includes an formatted indentation.
}, },
) )
- -
- -
-# Regression test for https://github.com/psf/black/issues/2015. # Regression test for https://github.com/psf/black/issues/2015.
run( run(
# fmt: off # fmt: off
[ @@ -22,8 +20,6 @@
@@ -22,9 +18,6 @@
+ path, + path,
check=True, check=True,
) )
- -
- -
-# Regression test for https://github.com/psf/black/issues/3026. # Regression test for https://github.com/psf/black/issues/3026.
def test_func(): def test_func():
# yapf: disable # yapf: disable
if unformatted( args ): @@ -34,8 +30,6 @@
@@ -34,9 +27,6 @@
return True return True
return False return False
- -
- -
-# Regression test for https://github.com/psf/black/issues/2567. # Regression test for https://github.com/psf/black/issues/2567.
if True: if True:
# fmt: off # fmt: off
for _ in range( 1 ): @@ -44,9 +38,7 @@
@@ -44,10 +34,7 @@
print ( "This won't be formatted" ) print ( "This won't be formatted" )
print ( "This won't be formatted either" ) print ( "This won't be formatted either" )
else: else:
- print("This will be formatted") - print("This will be formatted")
- -
- -
-# Regression test for https://github.com/psf/black/issues/3184.
+ print ( "This will be formatted" ) + print ( "This will be formatted" )
# Regression test for https://github.com/psf/black/issues/3184.
class A: class A:
async def call(param): async def call(param):
if param: @@ -61,27 +53,18 @@
@@ -61,27 +48,16 @@
elif param[0:4] in ("ZZZZ",): elif param[0:4] in ("ZZZZ",):
print ( "This won't be formatted either" ) print ( "This won't be formatted either" )
- print("This will be formatted") - print("This will be formatted")
- -
- -
-# Regression test for https://github.com/psf/black/issues/2985.
+ print ( "This will be formatted" ) + print ( "This will be formatted" )
# Regression test for https://github.com/psf/black/issues/2985.
class Named(t.Protocol): class Named(t.Protocol):
# fmt: off # fmt: off
@property @property
@ -166,8 +157,8 @@ elif unformatted:
- # fmt: on - # fmt: on
- -
- -
-# Regression test for https://github.com/psf/black/issues/3436.
+ def this_will_be_formatted ( self, **kwargs ) -> Named: ... + def this_will_be_formatted ( self, **kwargs ) -> Named: ...
# Regression test for https://github.com/psf/black/issues/3436.
if x: if x:
return x return x
# fmt: off # fmt: off
@ -181,6 +172,7 @@ elif unformatted:
## Ruff Output ## Ruff Output
```py ```py
# Regression test for https://github.com/psf/black/issues/3129.
setup( setup(
entry_points={ entry_points={
# fmt: off # fmt: off
@ -191,6 +183,7 @@ setup(
] # Includes an formatted indentation. ] # Includes an formatted indentation.
}, },
) )
# Regression test for https://github.com/psf/black/issues/2015.
run( run(
# fmt: off # fmt: off
[ [
@ -201,6 +194,7 @@ run(
+ path, + path,
check=True, check=True,
) )
# Regression test for https://github.com/psf/black/issues/3026.
def test_func(): def test_func():
# yapf: disable # yapf: disable
if unformatted( args ): if unformatted( args ):
@ -210,6 +204,7 @@ def test_func():
return True return True
return False return False
# Regression test for https://github.com/psf/black/issues/2567.
if True: if True:
# fmt: off # fmt: off
for _ in range( 1 ): for _ in range( 1 ):
@ -218,6 +213,7 @@ if True:
print ( "This won't be formatted either" ) print ( "This won't be formatted either" )
else: else:
print ( "This will be formatted" ) print ( "This will be formatted" )
# Regression test for https://github.com/psf/black/issues/3184.
class A: class A:
async def call(param): async def call(param):
if param: if param:
@ -232,12 +228,14 @@ class A:
print ( "This won't be formatted either" ) print ( "This won't be formatted either" )
print ( "This will be formatted" ) print ( "This will be formatted" )
# Regression test for https://github.com/psf/black/issues/2985.
class Named(t.Protocol): class Named(t.Protocol):
# fmt: off # fmt: off
@property @property
def this_wont_be_formatted ( self ) -> str: ... def this_wont_be_formatted ( self ) -> str: ...
class Factory(t.Protocol): class Factory(t.Protocol):
def this_will_be_formatted ( self, **kwargs ) -> Named: ... def this_will_be_formatted ( self, **kwargs ) -> Named: ...
# Regression test for https://github.com/psf/black/issues/3436.
if x: if x:
return x return x
# fmt: off # fmt: off

View file

@ -199,8 +199,8 @@ d={'a':1,
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,22 +1,11 @@ @@ -1,20 +1,17 @@
-#!/usr/bin/env python3 #!/usr/bin/env python3
import asyncio import asyncio
import sys import sys
- -
@ -208,28 +208,25 @@ d={'a':1,
- -
-from library import some_connection, some_decorator -from library import some_connection, some_decorator
- -
-# fmt: off
+from library import some_connection, \ +from library import some_connection, \
+ some_decorator + some_decorator
# fmt: off
from third_party import (X, from third_party import (X,
Y, Z) Y, Z)
-# fmt: on # fmt: on
-f"trigger 3.6 mode" -f"trigger 3.6 mode"
-# Comment 1
-
-# Comment 2
-
-
-# fmt: off
+f'trigger 3.6 mode' +f'trigger 3.6 mode'
# Comment 1
# Comment 2
-
# fmt: off
def func_no_args(): def func_no_args():
a; b; c @@ -39,72 +36,41 @@
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: def function_signature_stress_test(number:int,no_annotation=None,text:str='default',* ,debug:bool=False,**kwargs) -> str:
return text[number:-1] 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""): -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))) - offset = attr.ib(default=attr.Factory(lambda: _r.uniform(1, 2)))
- assert task._cancel_stack[: len(old_stack)] == old_stack - assert task._cancel_stack[: len(old_stack)] == old_stack
@ -308,7 +305,7 @@ d={'a':1,
def example(session): def example(session):
# fmt: off # fmt: off
result = session\ result = session\
@@ -113,9 +70,6 @@ @@ -113,9 +79,6 @@
models.Customer.email == email_address)\ models.Customer.email == email_address)\
.order_by(models.Customer.id.asc())\ .order_by(models.Customer.id.asc())\
.all() .all()
@ -318,23 +315,23 @@ d={'a':1,
def off_and_on_without_data(): def off_and_on_without_data():
"""All comments here are technically on the same prefix. """All comments here are technically on the same prefix.
@@ -123,12 +77,12 @@ @@ -123,12 +86,12 @@
""" """
# fmt: off # fmt: off
- # hey, that won't work - # hey, that won't work
- # fmt: on
- pass
+ #hey, that won't work + #hey, that won't work
+
+
+ # fmt: on # fmt: on
+ pass pass
-
-
def on_and_off_broken(): def on_and_off_broken():
"""Another known limitation.""" """Another known limitation."""
# fmt: on # fmt: on
@@ -137,21 +91,10 @@ @@ -137,21 +100,10 @@
and_=indeed . it is not formatted and_=indeed . it is not formatted
because . the . handling . inside . generate_ignored_nodes() because . the . handling . inside . generate_ignored_nodes()
now . considers . multiple . fmt . directives . within . one . prefix now . considers . multiple . fmt . directives . within . one . prefix
@ -357,7 +354,7 @@ d={'a':1,
) )
# fmt: off # fmt: off
a = ( a = (
@@ -182,24 +125,19 @@ @@ -182,24 +134,19 @@
re.MULTILINE|re.VERBOSE re.MULTILINE|re.VERBOSE
# fmt: on # fmt: on
) )
@ -372,7 +369,7 @@ d={'a':1,
+ (1, 2, 3, 4), + (1, 2, 3, 4),
+ (5, 6, 7, 8), + (5, 6, 7, 8),
+ (9, 10, 11, 12) + (9, 10, 11, 12)
+ } + } # yapf: disable
cfg.rule( cfg.rule(
- "Default", - "Default",
- "address", - "address",
@ -391,32 +388,36 @@ d={'a':1,
# fmt: off # fmt: off
xxxxxxx_xxxxxxxxxxxx={ xxxxxxx_xxxxxxxxxxxx={
"xxxxxxxx": { "xxxxxxxx": {
@@ -214,11 +152,9 @@ @@ -214,7 +161,7 @@
}, },
}, },
# fmt: on # fmt: on
- xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5, - xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5,
+ xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5 + xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5
) )
-# fmt: off # fmt: off
yield 'hello' yield 'hello'
-# No formatting to the end of the file
l=[1,2,3]
d={'a':1,
'b':2}
``` ```
## Ruff Output ## Ruff Output
```py ```py
#!/usr/bin/env python3
import asyncio import asyncio
import sys import sys
from third_party import X, Y, Z from third_party import X, Y, Z
from library import some_connection, \ from library import some_connection, \
some_decorator some_decorator
# fmt: off
from third_party import (X, from third_party import (X,
Y, Z) Y, Z)
# fmt: on
f'trigger 3.6 mode' f'trigger 3.6 mode'
# Comment 1
# Comment 2
# fmt: off
def func_no_args(): def func_no_args():
a; b; c a; b; c
if True: raise RuntimeError if True: raise RuntimeError
@ -438,6 +439,7 @@ many_args=[1,2,3]
) )
def function_signature_stress_test(number:int,no_annotation=None,text:str='default',* ,debug:bool=False,**kwargs) -> str: def function_signature_stress_test(number:int,no_annotation=None,text:str='default',* ,debug:bool=False,**kwargs) -> str:
return text[number:-1] 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''): 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))) offset = attr.ib(default=attr.Factory( lambda: _r.uniform(1, 2)))
assert task._cancel_stack[:len(old_stack)] == old_stack assert task._cancel_stack[:len(old_stack)] == old_stack
@ -542,7 +544,7 @@ def single_literal_yapf_disable():
(1, 2, 3, 4), (1, 2, 3, 4),
(5, 6, 7, 8), (5, 6, 7, 8),
(9, 10, 11, 12) (9, 10, 11, 12)
} } # yapf: disable
cfg.rule( cfg.rule(
"Default", "address", "Default", "address",
xxxx_xxxx=["xxx-xxxxxx-xxxxxxxxxx"], xxxx_xxxx=["xxx-xxxxxx-xxxxxxxxxx"],
@ -565,7 +567,9 @@ cfg.rule(
# fmt: on # fmt: on
xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5 xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5
) )
# fmt: off
yield 'hello' yield 'hello'
# No formatting to the end of the file
l=[1,2,3] l=[1,2,3]
d={'a':1, d={'a':1,
'b':2} 'b':2}

View file

@ -22,14 +22,13 @@ l3 = ["I have", "trailing comma", "so I should be braked",]
- "into multiple lines", - "into multiple lines",
- "because it is way too long", - "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 +l1 = ["This list should be broken up", "into multiple lines", "because it is way too long"]
l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"] # fmt: skip
-l3 = [ -l3 = [
- "I have", - "I have",
- "trailing comma", - "trailing comma",
- "so I should be braked", - "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",] +l3 = ["I have", "trailing comma", "so I should be braked",]
``` ```
@ -37,7 +36,7 @@ l3 = ["I have", "trailing comma", "so I should be braked",]
```py ```py
l1 = ["This list should be broken up", "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"] 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",] l3 = ["I have", "trailing comma", "so I should be braked",]
``` ```

View file

@ -20,15 +20,14 @@ f = ["This is a very long line that should be formatted into a clearer line ", "
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,10 +1,5 @@ @@ -1,10 +1,7 @@
-a = 3 -a = 3
-# fmt: off
+a = 3 +a = 3
# fmt: off
b, c = 1, 2 b, c = 1, 2
-d = 6 # fmt: skip d = 6 # fmt: skip
+d = 6
e = 5 e = 5
-# fmt: on # fmt: on
-f = [ -f = [
- "This is a very long line that should be formatted into a clearer line ", - "This is a very long line that should be formatted into a clearer line ",
- "by rearranging.", - "by rearranging.",
@ -40,9 +39,11 @@ f = ["This is a very long line that should be formatted into a clearer line ", "
```py ```py
a = 3 a = 3
# fmt: off
b, c = 1, 2 b, c = 1, 2
d = 6 d = 6 # fmt: skip
e = 5 e = 5
# fmt: on
f = ["This is a very long line that should be formatted into a clearer line ", "by rearranging."] f = ["This is a very long line that should be formatted into a clearer line ", "by rearranging."]
``` ```

View file

@ -16,15 +16,15 @@ l = [1, 2, 3,]
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,7 +1,2 @@ @@ -1,7 +1,3 @@
-a = 2 -a = 2
-# fmt: skip +a = 2
# fmt: skip
-l = [ -l = [
- 1, - 1,
- 2, - 2,
- 3, - 3,
-] -]
+a = 2
+l = [1, 2, 3,] +l = [1, 2, 3,]
``` ```
@ -32,6 +32,7 @@ l = [1, 2, 3,]
```py ```py
a = 2 a = 2
# fmt: skip
l = [1, 2, 3,] l = [1, 2, 3,]
``` ```

View file

@ -1,49 +0,0 @@
---
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
```

View file

@ -23,18 +23,18 @@ d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasu
-c = 9 # fmt: skip -c = 9 # fmt: skip
-d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" # fmt:skip -d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" # fmt:skip
+a = "this is some code" +a = "this is some code"
+b = 5 +b = 5 #fmt:skip
+c = 9 +c = 9 #fmt: skip
+d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" +d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" #fmt:skip
``` ```
## Ruff Output ## Ruff Output
```py ```py
a = "this is some code" a = "this is some code"
b = 5 b = 5 #fmt:skip
c = 9 c = 9 #fmt: skip
d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" #fmt:skip
``` ```
## Black Output ## Black Output

View file

@ -75,35 +75,32 @@ async def test_async_with():
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,17 +1,9 @@ @@ -2,15 +2,10 @@
-# Make sure a leading comment is not removed.
def some_func( unformatted, args ): # fmt: skip def some_func( unformatted, args ): # fmt: skip
print("I am some_func") print("I am some_func")
return 0 return 0
- # Make sure this comment is not removed. - # Make sure this comment is not removed.
- -
- -
-# Make sure a leading comment is not removed. # Make sure a leading comment is not removed.
async def some_async_func( unformatted, args): # fmt: skip async def some_async_func( unformatted, args): # fmt: skip
print("I am some_async_func") print("I am some_async_func")
await asyncio.sleep(1) await asyncio.sleep(1)
- -
- -
-# Make sure a leading comment is not removed. # Make sure a leading comment is not removed.
class SomeClass( Unformatted, SuperClasses ): # fmt: skip class SomeClass( Unformatted, SuperClasses ): # fmt: skip
def some_method( self, unformatted, args ): # fmt: skip def some_method( self, unformatted, args ): # fmt: skip
print("I am some_method") @@ -20,8 +15,6 @@
@@ -20,9 +12,6 @@
async def some_async_method( self, unformatted, args ): # fmt: skip async def some_async_method( self, unformatted, args ): # fmt: skip
print("I am some_async_method") print("I am some_async_method")
await asyncio.sleep(1) await asyncio.sleep(1)
- -
- -
-# Make sure a leading comment is not removed. # Make sure a leading comment is not removed.
if unformatted_call( args ): # fmt: skip if unformatted_call( args ): # fmt: skip
print("First branch") print("First branch")
# Make sure this is not removed. @@ -30,33 +23,21 @@
@@ -30,33 +19,21 @@
print("Second branch") print("Second branch")
else : # fmt: skip else : # fmt: skip
print("Last branch") print("Last branch")
@ -142,12 +139,15 @@ async def test_async_with():
## Ruff Output ## Ruff Output
```py ```py
# Make sure a leading comment is not removed.
def some_func( unformatted, args ): # fmt: skip def some_func( unformatted, args ): # fmt: skip
print("I am some_func") print("I am some_func")
return 0 return 0
# Make sure a leading comment is not removed.
async def some_async_func( unformatted, args): # fmt: skip async def some_async_func( unformatted, args): # fmt: skip
print("I am some_async_func") print("I am some_async_func")
await asyncio.sleep(1) await asyncio.sleep(1)
# Make sure a leading comment is not removed.
class SomeClass( Unformatted, SuperClasses ): # fmt: skip class SomeClass( Unformatted, SuperClasses ): # fmt: skip
def some_method( self, unformatted, args ): # fmt: skip def some_method( self, unformatted, args ): # fmt: skip
print("I am some_method") print("I am some_method")
@ -156,6 +156,7 @@ class SomeClass( Unformatted, SuperClasses ): # fmt: skip
async def some_async_method( self, unformatted, args ): # fmt: skip async def some_async_method( self, unformatted, args ): # fmt: skip
print("I am some_async_method") print("I am some_async_method")
await asyncio.sleep(1) await asyncio.sleep(1)
# Make sure a leading comment is not removed.
if unformatted_call( args ): # fmt: skip if unformatted_call( args ): # fmt: skip
print("First branch") print("First branch")
# Make sure this is not removed. # Make sure this is not removed.

View file

@ -1,42 +0,0 @@
---
source: crates/ruff_python_formatter/src/lib.rs
expression: snapshot
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip.py
---
## Input
```py
a, b = 1, 2
c = 6 # fmt: skip
d = 5
```
## Black Differences
```diff
--- Black
+++ Ruff
@@ -1,3 +1,3 @@
a, b = 1, 2
-c = 6 # fmt: skip
+c = 6
d = 5
```
## Ruff Output
```py
a, b = 1, 2
c = 6
d = 5
```
## Black Output
```py
a, b = 1, 2
c = 6 # fmt: skip
d = 5
```

View file

@ -108,8 +108,8 @@ def __await__(): return (yield)
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,100 +1,51 @@ @@ -1,100 +1,52 @@
-#!/usr/bin/env python3 #!/usr/bin/env python3
import asyncio import asyncio
import sys import sys
- -
@ -244,7 +244,7 @@ def __await__(): return (yield)
# trailing standalone comment # trailing standalone comment
) )
) )
@@ -117,23 +68,18 @@ @@ -117,23 +69,18 @@
\n? \n?
) )
$ $
@ -276,7 +276,7 @@ def __await__(): return (yield)
) -> A: ) -> A:
return ( return (
yield from A( yield from A(
@@ -142,7 +88,4 @@ @@ -142,7 +89,4 @@
**kwargs, **kwargs,
) )
) )
@ -290,6 +290,7 @@ def __await__(): return (yield)
## Ruff Output ## Ruff Output
```py ```py
#!/usr/bin/env python3
import asyncio import asyncio
import sys import sys
from third_party import X, Y, Z from third_party import X, Y, Z

View file

@ -74,7 +74,7 @@ some_module.some_function(
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,114 +1,42 @@ @@ -1,114 +1,46 @@
-def f( -def f(
- a, - a,
-): -):
@ -152,12 +152,12 @@ some_module.some_function(
- } - }
- -
- -
-# 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[ +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+]: +]:
+ json = {"k": {"k2": {"k3": [1,]}}} + 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() -> ( def some_function_with_a_really_long_name() -> (
returning_a_deeply_nested_import_of_a_type_i_suppose returning_a_deeply_nested_import_of_a_type_i_suppose
): ):
@ -192,12 +192,12 @@ some_module.some_function(
pass pass
- -
- -
-# Make sure inner one-element tuple won't explode # Make sure inner one-element tuple won't explode
some_module.some_function( some_module.some_function(
argument1, (one_element_tuple,), argument4, argument5, argument6 argument1, (one_element_tuple,), argument4, argument5, argument6
) )
- -
-# Inner trailing comma causes outer to explode # Inner trailing comma causes outer to explode
some_module.some_function( some_module.some_function(
- argument1, - argument1,
- ( - (
@ -233,6 +233,8 @@ def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
]: ]:
json = {"k": {"k2": {"k3": [1,]}}} 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() -> ( def some_function_with_a_really_long_name() -> (
returning_a_deeply_nested_import_of_a_type_i_suppose returning_a_deeply_nested_import_of_a_type_i_suppose
): ):
@ -250,9 +252,11 @@ def func() -> ((also_super_long_type_annotation_that_may_cause_an_AST_related_cr
)) ))
): ):
pass pass
# Make sure inner one-element tuple won't explode
some_module.some_function( some_module.some_function(
argument1, (one_element_tuple,), argument4, argument5, argument6 argument1, (one_element_tuple,), argument4, argument5, argument6
) )
# Inner trailing comma causes outer to explode
some_module.some_function( some_module.some_function(
argument1, (one, two,), argument4, argument5, argument6 argument1, (one, two,), argument4, argument5, argument6
) )

View file

@ -62,11 +62,11 @@ __all__ = (
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,55 +1,30 @@ @@ -1,55 +1,33 @@
"""The asyncio package, tracking PEP 3156.""" """The asyncio package, tracking PEP 3156."""
- -
-# flake8: noqa # flake8: noqa
-
-from logging import WARNING -from logging import WARNING
from logging import ( from logging import (
+ WARNING + WARNING
@ -76,19 +76,16 @@ __all__ = (
) )
import sys import sys
- -
-# This relies on each of the submodules having an __all__ variable. # This relies on each of the submodules having an __all__ variable.
from .base_events import * from .base_events import *
from .coroutines import * from .coroutines import *
-from .events import * # comment here from .events import * # comment here
- -
+from .events import *
from .futures import * from .futures import *
-from .locks import * # comment here from .locks import * # comment here
+from .locks import *
from .protocols import * from .protocols import *
- -
-from ..runners import * # comment here from ..runners import * # comment here
+from ..runners import *
from ..queues import * from ..queues import *
from ..streams import * from ..streams import *
- -
@ -136,6 +133,8 @@ __all__ = (
```py ```py
"""The asyncio package, tracking PEP 3156.""" """The asyncio package, tracking PEP 3156."""
# flake8: noqa
from logging import ( from logging import (
WARNING WARNING
) )
@ -143,13 +142,14 @@ from logging import (
ERROR, ERROR,
) )
import sys import sys
# This relies on each of the submodules having an __all__ variable.
from .base_events import * from .base_events import *
from .coroutines import * from .coroutines import *
from .events import * from .events import * # comment here
from .futures import * from .futures import *
from .locks import * from .locks import * # comment here
from .protocols import * from .protocols import *
from ..runners import * from ..runners import * # comment here
from ..queues import * from ..queues import *
from ..streams import * from ..streams import *
from some_library import ( from some_library import (

View file

@ -25,13 +25,12 @@ list_of_types = [tuple[int,],]
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,22 +1,6 @@ @@ -2,21 +2,9 @@
-# We should not treat the trailing comma # in a single-element subscript.
-# in a single-element subscript.
a: tuple[int,] a: tuple[int,]
b = 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[ -c: tuple[
- int, - int,
- int, - int,
@ -41,15 +40,15 @@ list_of_types = [tuple[int,],]
- int, - int,
-] -]
- -
-# Magic commas still work as expected for non-subscripts. +c: tuple[int, int,]
+d = tuple[int, int,]
# Magic commas still work as expected for non-subscripts.
-small_list = [ -small_list = [
- 1, - 1,
-] -]
-list_of_types = [ -list_of_types = [
- tuple[int,], - tuple[int,],
-] -]
+c: tuple[int, int,]
+d = tuple[int, int,]
+small_list = [1,] +small_list = [1,]
+list_of_types = [tuple[int,],] +list_of_types = [tuple[int,],]
``` ```
@ -57,10 +56,14 @@ list_of_types = [tuple[int,],]
## Ruff Output ## Ruff Output
```py ```py
# We should not treat the trailing comma
# in a single-element subscript.
a: tuple[int,] a: tuple[int,]
b = tuple[int,] b = tuple[int,]
# The magic comma still applies to multi-element subscripts.
c: tuple[int, int,] c: tuple[int, int,]
d = tuple[int, int,] d = tuple[int, int,]
# Magic commas still work as expected for non-subscripts.
small_list = [1,] small_list = [1,]
list_of_types = [tuple[int,],] list_of_types = [tuple[int,],]
``` ```

View file

@ -103,17 +103,16 @@ return np.divide(
a = 5.0**~4.0 a = 5.0**~4.0
b = 5.0 ** f() b = 5.0 ** f()
c = -(5.0**2.0) c = -(5.0**2.0)
@@ -47,9 +40,6 @@ @@ -47,8 +40,6 @@
o = settings(max_examples=10**6.0) o = settings(max_examples=10**6.0)
p = {(k, k**2): v**2.0 for k, v in pairs} p = {(k, k**2): v**2.0 for k, v in pairs}
q = [10.5**i for i in range(6)] q = [10.5**i for i in range(6)]
- -
- -
-# WE SHOULD DEFINITELY NOT EAT THESE COMMENTS (https://github.com/psf/black/issues/2873) # WE SHOULD DEFINITELY NOT EAT THESE COMMENTS (https://github.com/psf/black/issues/2873)
if hasattr(view, "sum_of_weights"): if hasattr(view, "sum_of_weights"):
return np.divide( # type: ignore[no-any-return] return np.divide( # type: ignore[no-any-return]
view.variance, # type: ignore[union-attr] @@ -57,7 +48,6 @@
@@ -57,7 +47,6 @@
out=np.full(view.sum_of_weights.shape, np.nan), # type: ignore[union-attr] 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] where=view.sum_of_weights**2 > view.sum_of_weights_squared, # type: ignore[union-attr]
) )
@ -168,6 +167,7 @@ n = count <= 10**5.0
o = settings(max_examples=10**6.0) o = settings(max_examples=10**6.0)
p = {(k, k**2): v**2.0 for k, v in pairs} p = {(k, k**2): v**2.0 for k, v in pairs}
q = [10.5**i for i in range(6)] 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"): if hasattr(view, "sum_of_weights"):
return np.divide( # type: ignore[no-any-return] return np.divide( # type: ignore[no-any-return]
view.variance, # type: ignore[union-attr] view.variance, # type: ignore[union-attr]

View file

@ -25,11 +25,10 @@ xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxx
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,21 +1,5 @@ @@ -2,20 +2,10 @@
-# 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
-# Left hand side fits in a single line but will still be exploded by the # magic trailing comma.
-# magic trailing comma.
-( -(
- first_value, - first_value,
- ( - (
@ -43,8 +42,8 @@ xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxx
arg2, arg2,
) )
- -
-# Make when when the left side of assignment plus the opening paren "... = (" is # 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. # exactly line length limit + 1, it won't be split like that.
-xxxxxxxxx_yyy_zzzzzzzz[ -xxxxxxxxx_yyy_zzzzzzzz[
- xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) - xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1)
-] = 1 -] = 1
@ -54,10 +53,16 @@ xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxx
## Ruff Output ## Ruff Output
```py ```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( first_value, (m1, m2,), third_value = xxxxxx_yyyyyy_zzzzzz_wwwwww_uuuuuuu_vvvvvvvvvvv(
arg1, arg1,
arg2, 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 xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1)] = 1
``` ```

View file

@ -94,16 +94,16 @@ async def main():
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,93 +1,56 @@ @@ -1,93 +1,64 @@
import asyncio import asyncio
- -
- -
-# Control example # Control example
async def main(): async def main():
await asyncio.sleep(1) await asyncio.sleep(1)
- -
- -
-# Remove brackets for short coroutine/task # Remove brackets for short coroutine/task
async def main(): async def main():
- await asyncio.sleep(1) - await asyncio.sleep(1)
- -
@ -120,40 +120,31 @@ async def main():
- await asyncio.sleep(1) - 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) + await (asyncio.sleep(1)
+ ) + )
# Check comments
async def main(): async def main():
- await asyncio.sleep(1) # Hello - await asyncio.sleep(1) # Hello
- -
- -
-# Long lines
+ await ( # Hello + await ( # Hello
+ asyncio.sleep(1) + asyncio.sleep(1)
+ ) + )
async def main(): async def main():
- await asyncio.gather( - await asyncio.sleep(1) # Hello
- asyncio.sleep(1), -
- asyncio.sleep(1), -
- asyncio.sleep(1),
- asyncio.sleep(1),
- asyncio.sleep(1),
- asyncio.sleep(1),
- asyncio.sleep(1),
+ await ( + await (
+ asyncio.sleep(1) # Hello + asyncio.sleep(1) # Hello
) + )
async def main():
- await asyncio.sleep(1) # Hello
- -
- -
-# Same as above but with magic trailing comma in function + await (
+ asyncio.sleep(1)
+ ) # Hello
# Long lines
async def main(): async def main():
- await asyncio.gather( - await asyncio.gather(
- asyncio.sleep(1), - asyncio.sleep(1),
@ -163,21 +154,29 @@ async def main():
- asyncio.sleep(1), - asyncio.sleep(1),
- asyncio.sleep(1), - asyncio.sleep(1),
- asyncio.sleep(1), - asyncio.sleep(1),
+ await ( - )
+ asyncio.sleep(1)
)
- -
- -
-# Cr@zY Br@ck3Tz + 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
async def main(): async def main():
- await black(1) - 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 ( + await (
+ ((((((((((((( + (((((((((((((
+ ((( ((( + ((( (((
@ -191,6 +190,7 @@ async def main():
+ ))) ))) + ))) )))
+ ))))))))))))) + )))))))))))))
+ ) + )
# Keep brackets around non power operations and nested awaits
async def main(): async def main():
await (set_of_tasks | other_set) await (set_of_tasks | other_set)
- -
@ -199,7 +199,7 @@ async def main():
await (await asyncio.sleep(1)) await (await asyncio.sleep(1))
- -
- -
-# It's awaits all the way down... # It's awaits all the way down...
async def main(): async def main():
await (await x) await (await x)
- -
@ -226,8 +226,10 @@ async def main():
```py ```py
import asyncio import asyncio
# Control example
async def main(): async def main():
await asyncio.sleep(1) await asyncio.sleep(1)
# Remove brackets for short coroutine/task
async def main(): async def main():
await (asyncio.sleep(1)) await (asyncio.sleep(1))
async def main(): async def main():
@ -237,6 +239,7 @@ async def main():
async def main(): async def main():
await (asyncio.sleep(1) await (asyncio.sleep(1)
) )
# Check comments
async def main(): async def main():
await ( # Hello await ( # Hello
asyncio.sleep(1) asyncio.sleep(1)
@ -248,11 +251,14 @@ async def main():
async def main(): async def main():
await ( await (
asyncio.sleep(1) asyncio.sleep(1)
) ) # Hello
# Long lines
async def main(): 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(): 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
async def main(): async def main():
await ( await (
((((((((((((( (((((((((((((
@ -267,10 +273,12 @@ async def main():
))) ))) ))) )))
))))))))))))) )))))))))))))
) )
# Keep brackets around non power operations and nested awaits
async def main(): async def main():
await (set_of_tasks | other_set) await (set_of_tasks | other_set)
async def main(): async def main():
await (await asyncio.sleep(1)) await (await asyncio.sleep(1))
# It's awaits all the way down...
async def main(): async def main():
await (await x) await (await x)
async def main(): async def main():

View file

@ -48,8 +48,8 @@ except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.ov
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,42 +1,24 @@ @@ -1,42 +1,25 @@
-# These brackets are redundant, therefore remove. # These brackets are redundant, therefore remove.
try: try:
a.something a.something
-except AttributeError as err: -except AttributeError as err:
@ -100,6 +100,7 @@ except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.ov
## Ruff Output ## Ruff Output
```py ```py
# These brackets are redundant, therefore remove.
try: try:
a.something a.something
except (AttributeError) as err: except (AttributeError) as err:

View file

@ -32,18 +32,18 @@ for (((((k, v))))) in d.items():
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,27 +1,11 @@ @@ -1,27 +1,15 @@
-# Only remove tuple brackets after `for` # Only remove tuple brackets after `for`
-for k, v in d.items(): -for k, v in d.items():
+for (k, v) in d.items(): +for (k, v) in d.items():
print(k, v) print(k, v)
- -
-# Don't touch tuple brackets after `in` # Don't touch tuple brackets after `in`
for module in (core, _unicodefun): for module in (core, _unicodefun):
if hasattr(module, "_verify_python3_env"): if hasattr(module, "_verify_python3_env"):
module._verify_python3_env = lambda: None module._verify_python3_env = lambda: None
- -
-# Brackets remain for long for loop lines # Brackets remain for long for loop lines
-for ( -for (
- why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long, - 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, - i_dont_know_but_we_should_still_check_the_behaviour_if_they_do,
@ -60,7 +60,7 @@ for (((((k, v))))) in d.items():
+for (k, v) in dfkasdjfldsjflkdsjflkdsjfdslkfjldsjfgkjdshgkljjdsfldgkhsdofudsfudsofajdslkfjdslkfjldisfjdffjsdlkfjdlkjjkdflskadjldkfjsalkfjdasj.items(): +for (k, v) in dfkasdjfldsjflkdsjflkdsjfdslkfjldsjfgkjdshgkljjdsfldgkhsdofudsfudsofajdslkfjdslkfjldisfjdffjsdlkfjdlkjjkdflskadjldkfjsalkfjdasj.items():
print(k, v) print(k, v)
- -
-# Test deeply nested brackets # Test deeply nested brackets
-for k, v in d.items(): -for k, v in d.items():
+for (((((k, v))))) in d.items(): +for (((((k, v))))) in d.items():
print(k, v) print(k, v)
@ -69,15 +69,19 @@ for (((((k, v))))) in d.items():
## Ruff Output ## Ruff Output
```py ```py
# Only remove tuple brackets after `for`
for (k, v) in d.items(): for (k, v) in d.items():
print(k, v) print(k, v)
# Don't touch tuple brackets after `in`
for module in (core, _unicodefun): for module in (core, _unicodefun):
if hasattr(module, "_verify_python3_env"): if hasattr(module, "_verify_python3_env"):
module._verify_python3_env = lambda: None 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(): 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) print(k, v)
for (k, v) in dfkasdjfldsjflkdsjflkdsjfdslkfjldsjfgkjdshgkljjdsfldgkhsdofudsfudsofajdslkfjdslkfjldisfjdffjsdlkfjdlkjjkdflskadjldkfjsalkfjdasj.items(): for (k, v) in dfkasdjfldsjflkdsjflkdsjfdslkfjldsjfgkjdshgkljjdsfldgkhsdofudsfudsofajdslkfjdslkfjldisfjdffjsdlkfjdlkjjkdflskadjldkfjsalkfjdasj.items():
print(k, v) print(k, v)
# Test deeply nested brackets
for (((((k, v))))) in d.items(): for (((((k, v))))) in d.items():
print(k, v) print(k, v)
``` ```

View file

@ -101,45 +101,33 @@ def foo() -> tuple[int, int, int,]:
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,120 +1,60 @@ @@ -1,120 +1,70 @@
-# Control # 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: def double(a: int) -> int:
- return 2 * a - return 2 * a
- -
- -
-# 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 + return 2*a
# Remove the brackets
-def double(a: int) -> int:
- return 2 * a
-
-
+def double(a: int) -> (int): +def double(a: int) -> (int):
+ return 2*a + 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) -> ( +def double(a: int) -> (
+ int): + int):
+ return 2*a + return 2*a
@ -150,17 +138,24 @@ def foo() -> tuple[int, int, int,]:
+ int + int
+): +):
+ return 2*a + return 2*a
# Don't lose the comments
-def double(a: int) -> int: # Hello
- return 2 * a
-
-
-def double(a: int) -> int: # Hello
- return 2 * a
-
-
+def double(a: int) -> ( # Hello +def double(a: int) -> ( # Hello
+ int + int
): +):
- return 2
-
-
+ return 2*a + return 2*a
+def double(a: int) -> ( +def double(a: int) -> (
+ int # Hello + int # Hello
+): +):
+ return 2*a + return 2*a
# Really long annotations
def foo() -> ( def foo() -> (
intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds
): ):
@ -169,10 +164,15 @@ def foo() -> tuple[int, int, int,]:
- -
-def foo() -> ( -def foo() -> (
- intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds - intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds
- | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds
-): -):
+def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: +def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds:
+ return 2 return 2
-
-
-def foo() -> (
- intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds
- | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds
-):
+def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: +def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds:
return 2 return 2
- -
@ -198,7 +198,7 @@ def foo() -> tuple[int, int, int,]:
return 2 return 2
- -
- -
-# Split args but no need to split return # Split args but no need to split return
-def foo( -def foo(
- a: int, - a: int,
- b: int, - b: int,
@ -208,8 +208,8 @@ def foo() -> tuple[int, int, int,]:
return 2 return 2
- -
- -
-# Deeply nested brackets # Deeply nested brackets
-# with *interesting* spacing # with *interesting* spacing
-def double(a: int) -> int: -def double(a: int) -> int:
- return 2 * a - return 2 * a
- -
@ -237,7 +237,7 @@ def foo() -> tuple[int, int, int,]:
return 2 return 2
- -
- -
-# Return type with commas # Return type with commas
-def foo() -> tuple[int, int, int]: -def foo() -> tuple[int, int, int]:
- return 2 - return 2
- -
@ -253,7 +253,9 @@ def foo() -> tuple[int, int, int,]:
return 2 return 2
- -
- -
-# Magic trailing comma example +def foo() -> tuple[loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong]:
+ return 2
# Magic trailing comma example
-def foo() -> ( -def foo() -> (
- tuple[ - tuple[
- int, - int,
@ -261,8 +263,6 @@ def foo() -> tuple[int, int, int,]:
- int, - int,
- ] - ]
-): -):
+def foo() -> tuple[loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong]:
+ return 2
+def foo() -> tuple[int, int, int,]: +def foo() -> tuple[int, int, int,]:
return 2 return 2
``` ```
@ -270,10 +270,13 @@ def foo() -> tuple[int, int, int,]:
## Ruff Output ## Ruff Output
```py ```py
# Control
def double(a: int) -> int: def double(a: int) -> int:
return 2*a return 2*a
# Remove the brackets
def double(a: int) -> (int): def double(a: int) -> (int):
return 2*a return 2*a
# Some newline variations
def double(a: int) -> ( def double(a: int) -> (
int): int):
return 2*a return 2*a
@ -284,6 +287,7 @@ def double(a: int) -> (
int int
): ):
return 2*a return 2*a
# Don't lose the comments
def double(a: int) -> ( # Hello def double(a: int) -> ( # Hello
int int
): ):
@ -292,6 +296,7 @@ def double(a: int) -> (
int # Hello int # Hello
): ):
return 2*a return 2*a
# Really long annotations
def foo() -> ( def foo() -> (
intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds
): ):
@ -304,8 +309,11 @@ def foo(a: int, b: int, c: int,) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasd
return 2 return 2
def foo(a: int, b: int, c: int,) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: def foo(a: int, b: int, c: int,) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds:
return 2 return 2
# Split args but no need to split return
def foo(a: int, b: int, c: int,) -> int: def foo(a: int, b: int, c: int,) -> int:
return 2 return 2
# Deeply nested brackets
# with *interesting* spacing
def double(a: int) -> (((((int))))): def double(a: int) -> (((((int))))):
return 2*a return 2*a
def double(a: int) -> ( def double(a: int) -> (
@ -322,12 +330,14 @@ def foo() -> (
) )
)): )):
return 2 return 2
# Return type with commas
def foo() -> ( def foo() -> (
tuple[int, int, int] tuple[int, int, int]
): ):
return 2 return 2
def foo() -> tuple[loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong]: def foo() -> tuple[loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong]:
return 2 return 2
# Magic trailing comma example
def foo() -> tuple[int, int, int,]: def foo() -> tuple[int, int, int,]:
return 2 return 2
``` ```

View file

@ -60,31 +60,31 @@ func(
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,25 +1,35 @@ @@ -1,25 +1,40 @@
-# We should not remove the trailing comma in a single-element subscript. # We should not remove the trailing comma in a single-element subscript.
a: tuple[int,] a: tuple[int,]
b = 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] -c: tuple[int, int]
-d = tuple[int, int] -d = tuple[int, int]
- -
-# Remove commas for non-subscripts. +c: tuple[int, int,]
+d = tuple[int, int,]
# Remove commas for non-subscripts.
-small_list = [1] -small_list = [1]
-list_of_types = [tuple[int,]] -list_of_types = [tuple[int,]]
-small_set = {1} -small_set = {1}
-set_of_types = {tuple[int,]} -set_of_types = {tuple[int,]}
- -
-# Except single element tuples
+c: tuple[int, int,]
+d = tuple[int, int,]
+small_list = [1,] +small_list = [1,]
+list_of_types = [tuple[int,],] +list_of_types = [tuple[int,],]
+small_set = {1,} +small_set = {1,}
+set_of_types = {tuple[int,],} +set_of_types = {tuple[int,],}
# Except single element tuples
small_tuple = (1,) 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) -zero(one).two(three).four(five)
- -
-func1(arg1).func2(arg2).func3(arg3).func4(arg4).func5(arg5) -func1(arg1).func2(arg2).func3(arg3).func4(arg4).func5(arg5)
@ -123,15 +123,20 @@ func(
## Ruff Output ## Ruff Output
```py ```py
# We should not remove the trailing comma in a single-element subscript.
a: tuple[int,] a: tuple[int,]
b = tuple[int,] b = tuple[int,]
# But commas in multiple element subscripts should be removed.
c: tuple[int, int,] c: tuple[int, int,]
d = tuple[int, int,] d = tuple[int, int,]
# Remove commas for non-subscripts.
small_list = [1,] small_list = [1,]
list_of_types = [tuple[int,],] list_of_types = [tuple[int,],]
small_set = {1,} small_set = {1,}
set_of_types = {tuple[int,],} set_of_types = {tuple[int,],}
# Except single element tuples
small_tuple = (1,) small_tuple = (1,)
# Trailing commas in multiple chained non-nested parens.
zero( zero(
one, one,
).two( ).two(

View file

@ -76,7 +76,7 @@ x[
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -17,19 +17,12 @@ @@ -17,19 +17,14 @@
slice[not so_simple : 1 < val <= 10] slice[not so_simple : 1 < val <= 10]
slice[(1 for i in range(42)) : x] slice[(1 for i in range(42)) : x]
slice[:: [i for i in range(42)]] slice[:: [i for i in range(42)]]
@ -86,17 +86,17 @@ x[
slice[await x : [i async for i in arange(42)] : 42] slice[await x : [i async for i in arange(42)] : 42]
- -
- -
-# These are from PEP-8: # These are from PEP-8:
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] 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:upper], ham[lower:upper:], ham[lower::step]
-# ham[lower+offset : upper+offset] # ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset] ham[lower + offset : upper + offset]
- -
slice[::, ::] slice[::, ::]
slice[ slice[
# A # A
@@ -46,7 +39,6 @@ @@ -46,7 +41,6 @@
# C # C
3 3
] ]
@ -104,7 +104,7 @@ x[
slice[ slice[
# A # A
1 1
@@ -56,4 +48,8 @@ @@ -56,4 +50,8 @@
# C # C
4 4
] ]
@ -140,8 +140,10 @@ slice[(1 for i in range(42)) : x]
slice[:: [i for i in range(42)]] slice[:: [i for i in range(42)]]
async def f(): async def f():
slice[await x : [i async for i in arange(42)] : 42] 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[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:upper], ham[lower:upper:], ham[lower::step]
# ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset] ham[lower + offset : upper + offset]
slice[::, ::] slice[::, ::]

View file

@ -33,9 +33,9 @@ def docstring_multiline():
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,19 +1,12 @@ @@ -1,19 +1,14 @@
-#!/usr/bin/env python3 #!/usr/bin/env python3
-
name = "Łukasz" name = "Łukasz"
-(f"hello {name}", f"hello {name}") -(f"hello {name}", f"hello {name}")
-(b"", b"") -(b"", b"")
@ -63,6 +63,8 @@ def docstring_multiline():
## Ruff Output ## Ruff Output
```py ```py
#!/usr/bin/env python3
name = "Łukasz" name = "Łukasz"
(f"hello {name}", F"hello {name}") (f"hello {name}", F"hello {name}")
(b"", B"") (b"", B"")

View file

@ -50,7 +50,7 @@ assert (
- ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 - ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525
-) # -) #
- -
+() << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 +() << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 #
assert sort_by_dependency( assert sort_by_dependency(
{ {
- "1": {"2", "3"}, - "1": {"2", "3"},
@ -70,7 +70,7 @@ assert (
-0 ^ 0 # -0 ^ 0 #
- -
- -
+0^0 +0^0 #
class A: class A:
def foo(self): def foo(self):
for _ in range(10): for _ in range(10):
@ -120,7 +120,7 @@ assert (
```py ```py
importA importA
() << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 () << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 #
assert sort_by_dependency( assert sort_by_dependency(
{ {
"1": {"2", "3"}, "2": {"2a", "2b"}, "3": {"3a", "3b"}, "1": {"2", "3"}, "2": {"2a", "2b"}, "3": {"3a", "3b"},
@ -129,7 +129,7 @@ assert sort_by_dependency(
) == ["2a", "2b", "2", "3a", "3b", "3", "1"] ) == ["2a", "2b", "2", "3a", "3b", "3", "1"]
importA importA
0 0
0^0 0^0 #
class A: class A:
def foo(self): def foo(self):
for _ in range(10): for _ in range(10):

View file

@ -46,7 +46,7 @@ assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx(
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,31 +1,7 @@ @@ -1,30 +1,8 @@
-zero( -zero(
- one, - one,
-).two( -).two(
@ -61,9 +61,9 @@ assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx(
- arg4, - arg4,
-).func5(arg5) -).func5(arg5)
- -
-# Inner one-element tuple shouldn't explode
+zero(one,).two(three,).four(five,) +zero(one,).two(three,).four(five,)
+func1(arg1).func2(arg2,).func3(arg3).func4(arg4,).func5(arg5) +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) func1(arg1).func2(arg1, (one_tuple,)).func3(arg3)
- -
-( -(
@ -76,28 +76,26 @@ assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx(
-) and func2(arg2) -) and func2(arg2)
- -
- -
-# Example from https://github.com/psf/black/issues/3229
+(a, b, c, d,) = func1(arg1) and func2(arg2) +(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): def refresh_token(self, device_family, refresh_token, api_key):
return self.orchestration.refresh_token( return self.orchestration.refresh_token(
data={ @@ -33,15 +11,12 @@
@@ -33,16 +9,10 @@
}, },
api_key=api_key, api_key=api_key,
)["extensions"]["sdk"]["token"] )["extensions"]["sdk"]["token"]
- -
- -
-# Edge case where a bug in a working-in-progress version of # Edge case where a bug in a working-in-progress version of
-# https://github.com/psf/black/pull/3370 causes an infinite recursion. # https://github.com/psf/black/pull/3370 causes an infinite recursion.
assert ( assert (
long_module.long_class.long_func().another_func() long_module.long_class.long_func().another_func()
== long_module.long_class.long_func()["some_key"].another_func(arg1) == long_module.long_class.long_func()["some_key"].another_func(arg1)
) )
- -
-# Regression test for https://github.com/psf/black/issues/3414. # Regression test for https://github.com/psf/black/issues/3414.
assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx(
xxxxxxxxx xxxxxxxxx
).xxxxxxxxxxxxxxxxxx(), (
``` ```
## Ruff Output ## Ruff Output
@ -105,8 +103,10 @@ assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx(
```py ```py
zero(one,).two(three,).four(five,) zero(one,).two(three,).four(five,)
func1(arg1).func2(arg2,).func3(arg3).func4(arg4,).func5(arg5) 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) func1(arg1).func2(arg1, (one_tuple,)).func3(arg3)
(a, b, c, d,) = func1(arg1) and func2(arg2) (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): def refresh_token(self, device_family, refresh_token, api_key):
return self.orchestration.refresh_token( return self.orchestration.refresh_token(
data={ data={
@ -114,10 +114,13 @@ def refresh_token(self, device_family, refresh_token, api_key):
}, },
api_key=api_key, api_key=api_key,
)["extensions"]["sdk"]["token"] )["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 ( assert (
long_module.long_class.long_func().another_func() long_module.long_class.long_func().another_func()
== long_module.long_class.long_func()["some_key"].another_func(arg1) == 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( assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx(
xxxxxxxxx xxxxxxxxx
).xxxxxxxxxxxxxxxxxx(), ( ).xxxxxxxxxxxxxxxxxx(), (

View file

@ -20,8 +20,8 @@ this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890")
```diff ```diff
--- Black --- Black
+++ Ruff +++ Ruff
@@ -1,12 +1,3 @@ @@ -1,12 +1,5 @@
-# This is a standalone comment. # This is a standalone comment.
-( -(
- sdfjklsdfsjldkflkjsf, - sdfjklsdfsjldkflkjsf,
- sdfjsdfjlksdljkfsdlkf, - sdfjsdfjlksdljkfsdlkf,
@ -29,10 +29,10 @@ this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890")
- sdsfsdfjskdflsfsdf, - sdsfsdfjskdflsfsdf,
-) = (1, 2, 3) -) = (1, 2, 3)
- -
-# This is as well. +sdfjklsdfsjldkflkjsf, sdfjsdfjlksdljkfsdlkf, sdfsdjfklsdfjlksdljkf, sdsfsdfjskdflsfsdf = 1, 2, 3
# This is as well.
-(this_will_be_wrapped_in_parens,) = struct.unpack(b"12345678901234567890") -(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") +this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890")
(a,) = call() (a,) = call()
``` ```
@ -40,7 +40,9 @@ this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890")
## Ruff Output ## Ruff Output
```py ```py
# This is a standalone comment.
sdfjklsdfsjldkflkjsf, sdfjsdfjlksdljkfsdlkf, sdfsdjfklsdfjlksdljkf, sdsfsdfjskdflsfsdf = 1, 2, 3 sdfjklsdfsjldkflkjsf, sdfjsdfjlksdljkfsdlkf, sdfsdjfklsdfjlksdljkf, sdsfsdfjskdflsfsdf = 1, 2, 3
# This is as well.
this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890") this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890")
(a,) = call() (a,) = call()
``` ```

View file

@ -68,9 +68,33 @@ pub(crate) fn lines_before(code: &str, offset: TextSize) -> u32 {
newlines newlines
} }
/// Counts the empty lines between `offset` and the first non-whitespace character.
pub(crate) fn lines_after(code: &str, offset: TextSize) -> u32 {
let rest = &code[usize::from(offset)..];
let mut newlines = 0;
for (index, c) in rest.char_indices() {
match c {
'\n' => {
newlines += 1;
}
'\r' if rest.as_bytes().get(index + 1).copied() == Some(b'\n') => {
continue;
}
'\r' => {
newlines += 1;
}
c if is_python_whitespace(c) => continue,
_ => break,
}
}
newlines
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::trivia::lines_before; use crate::trivia::{lines_after, lines_before};
use ruff_text_size::TextSize; use ruff_text_size::TextSize;
#[test] #[test]
@ -113,4 +137,37 @@ mod tests {
1 1
); );
} }
#[test]
fn lines_after_empty_string() {
assert_eq!(lines_after("", TextSize::new(0)), 0);
}
#[test]
fn lines_after_in_the_middle_of_a_line() {
assert_eq!(lines_after("a = 20", TextSize::new(4)), 0);
}
#[test]
fn lines_after_before_a_new_line() {
assert_eq!(lines_after("a = 20\nb = 10", TextSize::new(6)), 1);
}
#[test]
fn lines_after_multiple_newlines() {
assert_eq!(lines_after("a = 20\n\r\nb = 10", TextSize::new(6)), 2);
}
#[test]
fn lines_after_before_comment_offset() {
assert_eq!(lines_after("a = 20 # a comment\n", TextSize::new(7)), 0);
}
#[test]
fn lines_after_with_comment_only_line() {
assert_eq!(
lines_after("a = 20\n# some comment\nb = 10", TextSize::new(6)),
1
);
}
} }