diff --git a/Cargo.lock b/Cargo.lock index 11293fa2b7..47bc9299be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2110,6 +2110,7 @@ dependencies = [ "insta", "once_cell", "ruff_formatter", + "ruff_python", "ruff_rustpython", "ruff_testing_macros", "ruff_text_size", diff --git a/crates/ruff/src/docstrings/constants.rs b/crates/ruff/src/docstrings/constants.rs deleted file mode 100644 index 3473b0c841..0000000000 --- a/crates/ruff/src/docstrings/constants.rs +++ /dev/null @@ -1,13 +0,0 @@ -/// See: - -pub const TRIPLE_QUOTE_PREFIXES: &[&str] = &[ - "u\"\"\"", "u'''", "r\"\"\"", "r'''", "U\"\"\"", "U'''", "R\"\"\"", "R'''", "\"\"\"", "'''", -]; - -pub const SINGLE_QUOTE_PREFIXES: &[&str] = &[ - "u\"", "u'", "r\"", "r'", "u\"", "u'", "r\"", "r'", "U\"", "U'", "R\"", "R'", "\"", "'", -]; - -pub const TRIPLE_QUOTE_SUFFIXES: &[&str] = &["\"\"\"", "'''"]; - -pub const SINGLE_QUOTE_SUFFIXES: &[&str] = &["\"", "'"]; diff --git a/crates/ruff/src/docstrings/mod.rs b/crates/ruff/src/docstrings/mod.rs index 6c0f85022a..468f9f4608 100644 --- a/crates/ruff/src/docstrings/mod.rs +++ b/crates/ruff/src/docstrings/mod.rs @@ -1,4 +1,3 @@ -pub mod constants; pub mod definition; pub mod extraction; pub mod google; diff --git a/crates/ruff/src/rules/pydocstyle/helpers.rs b/crates/ruff/src/rules/pydocstyle/helpers.rs index 18c8f1b2c0..8d08cb5810 100644 --- a/crates/ruff/src/rules/pydocstyle/helpers.rs +++ b/crates/ruff/src/rules/pydocstyle/helpers.rs @@ -1,13 +1,15 @@ -use crate::docstrings::constants; +use ruff_python::string::{ + SINGLE_QUOTE_PREFIXES, SINGLE_QUOTE_SUFFIXES, TRIPLE_QUOTE_PREFIXES, TRIPLE_QUOTE_SUFFIXES, +}; /// Strip the leading and trailing quotes from a docstring. pub fn raw_contents(contents: &str) -> &str { - for pattern in constants::TRIPLE_QUOTE_PREFIXES { + for pattern in TRIPLE_QUOTE_PREFIXES { if contents.starts_with(pattern) { return &contents[pattern.len()..contents.len() - 3]; } } - for pattern in constants::SINGLE_QUOTE_PREFIXES { + for pattern in SINGLE_QUOTE_PREFIXES { if contents.starts_with(pattern) { return &contents[pattern.len()..contents.len() - 1]; } @@ -18,10 +20,7 @@ pub fn raw_contents(contents: &str) -> &str { /// Return the leading quote string for a docstring (e.g., `"""`). pub fn leading_quote(content: &str) -> Option<&str> { if let Some(first_line) = content.lines().next() { - for pattern in constants::TRIPLE_QUOTE_PREFIXES - .iter() - .chain(constants::SINGLE_QUOTE_PREFIXES) - { + for pattern in TRIPLE_QUOTE_PREFIXES.iter().chain(SINGLE_QUOTE_PREFIXES) { if first_line.starts_with(pattern) { return Some(pattern); } @@ -32,9 +31,9 @@ pub fn leading_quote(content: &str) -> Option<&str> { /// Return the trailing quote string for a docstring (e.g., `"""`). pub fn trailing_quote(content: &str) -> Option<&&str> { - constants::TRIPLE_QUOTE_SUFFIXES + TRIPLE_QUOTE_SUFFIXES .iter() - .chain(constants::SINGLE_QUOTE_SUFFIXES) + .chain(SINGLE_QUOTE_SUFFIXES) .find(|&pattern| content.ends_with(pattern)) } diff --git a/crates/ruff/src/rules/pydocstyle/rules/multi_line_summary_start.rs b/crates/ruff/src/rules/pydocstyle/rules/multi_line_summary_start.rs index 1d840673ab..25f72523b5 100644 --- a/crates/ruff/src/rules/pydocstyle/rules/multi_line_summary_start.rs +++ b/crates/ruff/src/rules/pydocstyle/rules/multi_line_summary_start.rs @@ -1,9 +1,9 @@ use ruff_macros::{define_violation, derive_message_formats}; +use ruff_python::string::TRIPLE_QUOTE_PREFIXES; use crate::ast::types::Range; use crate::ast::whitespace::LinesWithTrailingNewline; use crate::checkers::ast::Checker; -use crate::docstrings::constants; use crate::docstrings::definition::{DefinitionKind, Docstring}; use crate::fix::Fix; use crate::message::Location; @@ -54,7 +54,7 @@ pub fn multi_line_summary_start(checker: &mut Checker, docstring: &Docstring) { { return; }; - if constants::TRIPLE_QUOTE_PREFIXES.contains(&first_line) { + if TRIPLE_QUOTE_PREFIXES.contains(&first_line) { if checker .settings .rules diff --git a/crates/ruff_python/src/string.rs b/crates/ruff_python/src/string.rs index 16499f2182..c780abf496 100644 --- a/crates/ruff_python/src/string.rs +++ b/crates/ruff_python/src/string.rs @@ -1,6 +1,16 @@ use once_cell::sync::Lazy; use regex::Regex; +/// See: +pub const TRIPLE_QUOTE_PREFIXES: &[&str] = &[ + "u\"\"\"", "u'''", "r\"\"\"", "r'''", "U\"\"\"", "U'''", "R\"\"\"", "R'''", "\"\"\"", "'''", +]; +pub const SINGLE_QUOTE_PREFIXES: &[&str] = &[ + "u\"", "u'", "r\"", "r'", "u\"", "u'", "r\"", "r'", "U\"", "U'", "R\"", "R'", "\"", "'", +]; +pub const TRIPLE_QUOTE_SUFFIXES: &[&str] = &["\"\"\"", "'''"]; +pub const SINGLE_QUOTE_SUFFIXES: &[&str] = &["\"", "'"]; + pub static STRING_QUOTE_PREFIX_REGEX: Lazy = Lazy::new(|| Regex::new(r#"^(?i)[urb]*['"](?P.*)['"]$"#).unwrap()); diff --git a/crates/ruff_python_formatter/Cargo.toml b/crates/ruff_python_formatter/Cargo.toml index 5d08682017..5894322556 100644 --- a/crates/ruff_python_formatter/Cargo.toml +++ b/crates/ruff_python_formatter/Cargo.toml @@ -7,6 +7,7 @@ rust-version = { workspace = true } [dependencies] ruff_formatter = { path = "../ruff_formatter" } +ruff_python = { path = "../ruff_python" } ruff_rustpython = { path = "../ruff_rustpython" } ruff_text_size = { path = "../ruff_text_size" } diff --git a/crates/ruff_python_formatter/src/core/helpers.rs b/crates/ruff_python_formatter/src/core/helpers.rs new file mode 100644 index 0000000000..dd7dc2db08 --- /dev/null +++ b/crates/ruff_python_formatter/src/core/helpers.rs @@ -0,0 +1,23 @@ +use ruff_python::string::{ + SINGLE_QUOTE_PREFIXES, SINGLE_QUOTE_SUFFIXES, TRIPLE_QUOTE_PREFIXES, TRIPLE_QUOTE_SUFFIXES, +}; + +/// Return the leading quote string for a docstring (e.g., `"""`). +pub fn leading_quote(content: &str) -> Option<&str> { + if let Some(first_line) = content.lines().next() { + for pattern in TRIPLE_QUOTE_PREFIXES.iter().chain(SINGLE_QUOTE_PREFIXES) { + if first_line.starts_with(pattern) { + return Some(pattern); + } + } + } + None +} + +/// Return the trailing quote string for a docstring (e.g., `"""`). +pub fn trailing_quote(content: &str) -> Option<&&str> { + TRIPLE_QUOTE_SUFFIXES + .iter() + .chain(SINGLE_QUOTE_SUFFIXES) + .find(|&pattern| content.ends_with(pattern)) +} diff --git a/crates/ruff_python_formatter/src/core/mod.rs b/crates/ruff_python_formatter/src/core/mod.rs index 3812c9abd1..927be4f07d 100644 --- a/crates/ruff_python_formatter/src/core/mod.rs +++ b/crates/ruff_python_formatter/src/core/mod.rs @@ -1,3 +1,4 @@ +pub mod helpers; pub mod locator; pub mod types; pub mod visitor; diff --git a/crates/ruff_python_formatter/src/format/expr.rs b/crates/ruff_python_formatter/src/format/expr.rs index 240270cfe2..d14ceda8bd 100644 --- a/crates/ruff_python_formatter/src/format/expr.rs +++ b/crates/ruff_python_formatter/src/format/expr.rs @@ -13,6 +13,7 @@ use crate::cst::{ Arguments, Boolop, Cmpop, Comprehension, Expr, ExprKind, Keyword, Operator, Unaryop, }; use crate::format::helpers::{is_self_closing, is_simple_power, is_simple_slice}; +use crate::format::strings::string_literal; use crate::shared_traits::AsFormat; use crate::trivia::{Parenthesize, Relationship, TriviaKind}; @@ -128,8 +129,6 @@ fn format_tuple( write!( f, [soft_block_indent(&format_with(|f| { - // TODO(charlie): If the magic trailing comma isn't present, and the - // tuple is _already_ expanded, we're not supposed to add this. let magic_trailing_comma = expr .trivia .iter() @@ -641,10 +640,21 @@ fn format_joined_str( fn format_constant( f: &mut Formatter>, expr: &Expr, - _constant: &Constant, + constant: &Constant, _kind: Option<&str>, ) -> FormatResult<()> { - write!(f, [literal(Range::from_located(expr))])?; + match constant { + Constant::None => write!(f, [text("None")])?, + Constant::Bool(value) => { + if *value { + write!(f, [text("True")])?; + } else { + write!(f, [text("False")])?; + } + } + Constant::Str(_) => write!(f, [string_literal(expr)])?, + _ => write!(f, [literal(Range::from_located(expr))])?, + } Ok(()) } diff --git a/crates/ruff_python_formatter/src/format/mod.rs b/crates/ruff_python_formatter/src/format/mod.rs index ca673053b3..5dae9fd406 100644 --- a/crates/ruff_python_formatter/src/format/mod.rs +++ b/crates/ruff_python_formatter/src/format/mod.rs @@ -10,5 +10,6 @@ mod expr; mod helpers; mod operator; mod stmt; +mod strings; mod unaryop; mod withitem; diff --git a/crates/ruff_python_formatter/src/format/strings.rs b/crates/ruff_python_formatter/src/format/strings.rs new file mode 100644 index 0000000000..25c602f637 --- /dev/null +++ b/crates/ruff_python_formatter/src/format/strings.rs @@ -0,0 +1,240 @@ +use rustpython_parser::{Mode, Tok}; + +use ruff_formatter::prelude::*; +use ruff_formatter::{write, Format}; +use ruff_text_size::TextSize; + +use crate::context::ASTFormatContext; +use crate::core::helpers::{leading_quote, trailing_quote}; +use crate::core::types::Range; +use crate::cst::Expr; +use crate::trivia::Parenthesize; + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct StringLiteralPart { + range: Range, +} + +impl Format> for StringLiteralPart { + fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { + let (source, start, end) = f.context().locator().slice(self.range); + + // Extract leading and trailing quotes. + let content = &source[start..end]; + let leading_quote = leading_quote(content).unwrap(); + let trailing_quote = trailing_quote(content).unwrap(); + let body = &content[leading_quote.len()..content.len() - trailing_quote.len()]; + + // Determine the correct quote style. + // TODO(charlie): Make this parameterizable. + let mut squotes: usize = 0; + let mut dquotes: usize = 0; + for char in body.chars() { + if char == '\'' { + squotes += 1; + } else if char == '"' { + dquotes += 1; + } + } + + let mut is_raw = false; + if leading_quote.contains('r') { + is_raw = true; + f.write_element(FormatElement::StaticText { text: "r" })?; + } else if leading_quote.contains('R') { + is_raw = true; + f.write_element(FormatElement::StaticText { text: "R" })?; + } + + if trailing_quote.len() == 1 { + // Single-quoted string. + if dquotes == 0 || squotes > 0 { + // If the body doesn't contain any double quotes, or it contains both single and + // double quotes, use double quotes. + f.write_element(FormatElement::StaticText { text: "\"" })?; + f.write_element(FormatElement::DynamicText { + text: if is_raw { + body.into() + } else { + double_escape(body).into() + }, + source_position: TextSize::default(), + })?; + f.write_element(FormatElement::StaticText { text: "\"" })?; + Ok(()) + } else { + f.write_element(FormatElement::StaticText { text: "'" })?; + f.write_element(FormatElement::DynamicText { + text: if is_raw { + body.into() + } else { + single_escape(body).into() + }, + source_position: TextSize::default(), + })?; + f.write_element(FormatElement::StaticText { text: "'" })?; + Ok(()) + } + } else if trailing_quote.len() == 3 { + // Triple-quoted string. + if body.starts_with("\"\"\"") || body.ends_with('"') { + // We only need to use single quotes if the string body starts with three or more + // double quotes, or ends with a double quote. Converting to double quotes in those + // cases would cause a syntax error. + f.write_element(FormatElement::StaticText { text: "'''" })?; + f.write_element(FormatElement::DynamicText { + text: body.to_string().into_boxed_str(), + source_position: TextSize::default(), + })?; + f.write_element(FormatElement::StaticText { text: "'''" })?; + Ok(()) + } else { + f.write_element(FormatElement::StaticText { text: "\"\"\"" })?; + f.write_element(FormatElement::DynamicText { + text: body.to_string().into_boxed_str(), + source_position: TextSize::default(), + })?; + f.write_element(FormatElement::StaticText { text: "\"\"\"" })?; + Ok(()) + } + } else { + unreachable!("Invalid quote length: {}", trailing_quote.len()); + } + } +} + +#[inline] +pub const fn string_literal_part(range: Range) -> StringLiteralPart { + StringLiteralPart { range } +} + +#[derive(Debug, Copy, Clone)] +pub struct StringLiteral<'a> { + expr: &'a Expr, +} + +impl Format> for StringLiteral<'_> { + fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { + let expr = self.expr; + + // TODO(charlie): This tokenization needs to happen earlier, so that we can attach + // comments to individual string literals. + let (source, start, end) = f.context().locator().slice(Range::from_located(expr)); + let elts = + rustpython_parser::lexer::lex_located(&source[start..end], Mode::Module, expr.location) + .flatten() + .filter_map(|(start, tok, end)| { + if matches!(tok, Tok::String { .. }) { + Some(Range::new(start, end)) + } else { + None + } + }) + .collect::>(); + write!( + f, + [group(&format_with(|f| { + if matches!(expr.parentheses, Parenthesize::IfExpanded) { + write!(f, [if_group_breaks(&text("("))])?; + } + for (i, elt) in elts.iter().enumerate() { + write!(f, [string_literal_part(*elt)])?; + if i < elts.len() - 1 { + write!(f, [soft_line_break_or_space()])?; + } + } + if matches!(expr.parentheses, Parenthesize::IfExpanded) { + write!(f, [if_group_breaks(&text(")"))])?; + } + Ok(()) + }))] + )?; + Ok(()) + } +} + +#[inline] +pub const fn string_literal(expr: &Expr) -> StringLiteral { + StringLiteral { expr } +} + +/// Escape a string body to be used in a string literal with double quotes. +fn double_escape(text: &str) -> String { + let mut escaped = String::with_capacity(text.len()); + let mut chars = text.chars().peekable(); + while let Some(ch) = chars.next() { + if ch == '\\' { + let Some(next) = chars.peek() else { + break; + }; + if *next == '\'' { + chars.next(); + escaped.push('\''); + } else if *next == '"' { + chars.next(); + escaped.push('"'); + } else if *next == '\\' { + chars.next(); + escaped.push('\\'); + escaped.push(ch); + } else { + escaped.push(ch); + } + } else if ch == '"' { + escaped.push('\\'); + escaped.push('"'); + } else { + escaped.push(ch); + } + } + escaped +} + +/// Escape a string body to be used in a string literal with single quotes. +fn single_escape(text: &str) -> String { + let mut escaped = String::with_capacity(text.len()); + let mut chars = text.chars().peekable(); + while let Some(ch) = chars.next() { + if ch == '\\' { + let Some(next) = chars.peek() else { + break; + }; + if *next == '"' { + chars.next(); + escaped.push('"'); + } else if *next == '\'' { + chars.next(); + escaped.push('\''); + } else if *next == '\\' { + chars.next(); + escaped.push('\\'); + escaped.push(ch); + } else { + escaped.push(ch); + } + } else if ch == '\'' { + escaped.push('\\'); + escaped.push('\''); + } else { + escaped.push(ch); + } + } + escaped +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_double_escape() { + assert_eq!(double_escape(r#"It\'s mine"#), r#"It's mine"#); + assert_eq!(double_escape(r#"It\'s "mine""#), r#"It's \"mine\""#); + assert_eq!(double_escape(r#"It\\'s mine"#), r#"It\\'s mine"#); + } + + #[test] + fn test_single_escape() { + assert_eq!(single_escape(r#"It's \"mine\""#), r#"It\'s "mine""#); + } +} diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__bracketmatch_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__bracketmatch_py.snap deleted file mode 100644 index c142929ea6..0000000000 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__bracketmatch_py.snap +++ /dev/null @@ -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/bracketmatch.py ---- -## Input - -```py -for ((x in {}) or {})['a'] in x: - pass -pem_spam = lambda l, spam = { - "x": 3 -}: not spam.get(l.strip()) -lambda x=lambda y={1: 3}: y['x':lambda y: {1: 2}]: x -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -1,4 +1,4 @@ --for ((x in {}) or {})["a"] in x: -+for ((x in {}) or {})['a'] in x: - pass - pem_spam = lambda l, spam={"x": 3}: not spam.get(l.strip()) --lambda x=lambda y={1: 3}: y["x" : lambda y: {1: 2}]: x -+lambda x=lambda y={1: 3}: y['x' : lambda y: {1: 2}]: x -``` - -## Ruff Output - -```py -for ((x in {}) or {})['a'] in x: - pass -pem_spam = lambda l, spam={"x": 3}: not spam.get(l.strip()) -lambda x=lambda y={1: 3}: y['x' : lambda y: {1: 2}]: x -``` - -## Black Output - -```py -for ((x in {}) or {})["a"] in x: - pass -pem_spam = lambda l, spam={"x": 3}: not spam.get(l.strip()) -lambda x=lambda y={1: 3}: y["x" : lambda y: {1: 2}]: x -``` - - diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__collections_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__collections_py.snap deleted file mode 100644 index d35a56bb0e..0000000000 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__collections_py.snap +++ /dev/null @@ -1,319 +0,0 @@ ---- -source: crates/ruff_python_formatter/src/lib.rs -expression: snapshot -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/collections.py ---- -## Input - -```py -import core, time, a - -from . import A, B, C - -# keeps existing trailing comma -from foo import ( - bar, -) - -# also keeps existing structure -from foo import ( - baz, - qux, -) - -# `as` works as well -from foo import ( - xyzzy as magic, -) - -a = {1,2,3,} -b = { -1,2, - 3} -c = { - 1, - 2, - 3, -} -x = 1, -y = narf(), -nested = {(1,2,3),(4,5,6),} -nested_no_trailing_comma = {(1,2,3),(4,5,6)} -nested_long_lines = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "cccccccccccccccccccccccccccccccccccccccc", (1, 2, 3), "dddddddddddddddddddddddddddddddddddddddd"] -{"oneple": (1,),} -{"oneple": (1,)} -['ls', 'lsoneple/%s' % (foo,)] -x = {"oneple": (1,)} -y = {"oneple": (1,),} -assert False, ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wraps %s" % bar) - -# looping over a 1-tuple should also not get wrapped -for x in (1,): - pass -for (x,) in (1,), (2,), (3,): - pass - -[1, 2, 3,] - -division_result_tuple = (6/2,) -print("foo %r", (foo.bar,)) - -if True: - IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING = ( - Config.IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING - | {pylons.controllers.WSGIController} - ) - -if True: - ec2client.get_waiter('instance_stopped').wait( - InstanceIds=[instance.id], - WaiterConfig={ - 'Delay': 5, - }) - ec2client.get_waiter("instance_stopped").wait( - InstanceIds=[instance.id], - WaiterConfig={"Delay": 5,}, - ) - ec2client.get_waiter("instance_stopped").wait( - InstanceIds=[instance.id], WaiterConfig={"Delay": 5,}, - ) -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -47,7 +47,7 @@ - "oneple": (1,), - } - {"oneple": (1,)} --["ls", "lsoneple/%s" % (foo,)] -+['ls', 'lsoneple/%s' % (foo,)] - x = {"oneple": (1,)} - y = { - "oneple": (1,), -@@ -79,10 +79,10 @@ - ) - - if True: -- ec2client.get_waiter("instance_stopped").wait( -+ ec2client.get_waiter('instance_stopped').wait( - InstanceIds=[instance.id], - WaiterConfig={ -- "Delay": 5, -+ 'Delay': 5, - }, - ) - ec2client.get_waiter("instance_stopped").wait( -``` - -## Ruff Output - -```py -import core, time, a - -from . import A, B, C - -# keeps existing trailing comma -from foo import ( - bar, -) - -# also keeps existing structure -from foo import ( - baz, - qux, -) - -# `as` works as well -from foo import ( - xyzzy as magic, -) - -a = { - 1, - 2, - 3, -} -b = {1, 2, 3} -c = { - 1, - 2, - 3, -} -x = (1,) -y = (narf(),) -nested = { - (1, 2, 3), - (4, 5, 6), -} -nested_no_trailing_comma = {(1, 2, 3), (4, 5, 6)} -nested_long_lines = [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - "cccccccccccccccccccccccccccccccccccccccc", - (1, 2, 3), - "dddddddddddddddddddddddddddddddddddddddd", -] -{ - "oneple": (1,), -} -{"oneple": (1,)} -['ls', 'lsoneple/%s' % (foo,)] -x = {"oneple": (1,)} -y = { - "oneple": (1,), -} -assert False, ( - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wraps %s" - % bar -) - -# looping over a 1-tuple should also not get wrapped -for x in (1,): - pass -for (x,) in (1,), (2,), (3,): - pass - -[ - 1, - 2, - 3, -] - -division_result_tuple = (6 / 2,) -print("foo %r", (foo.bar,)) - -if True: - IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING = ( - Config.IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING - | {pylons.controllers.WSGIController} - ) - -if True: - ec2client.get_waiter('instance_stopped').wait( - InstanceIds=[instance.id], - WaiterConfig={ - 'Delay': 5, - }, - ) - ec2client.get_waiter("instance_stopped").wait( - InstanceIds=[instance.id], - WaiterConfig={ - "Delay": 5, - }, - ) - ec2client.get_waiter("instance_stopped").wait( - InstanceIds=[instance.id], - WaiterConfig={ - "Delay": 5, - }, - ) -``` - -## Black Output - -```py -import core, time, a - -from . import A, B, C - -# keeps existing trailing comma -from foo import ( - bar, -) - -# also keeps existing structure -from foo import ( - baz, - qux, -) - -# `as` works as well -from foo import ( - xyzzy as magic, -) - -a = { - 1, - 2, - 3, -} -b = {1, 2, 3} -c = { - 1, - 2, - 3, -} -x = (1,) -y = (narf(),) -nested = { - (1, 2, 3), - (4, 5, 6), -} -nested_no_trailing_comma = {(1, 2, 3), (4, 5, 6)} -nested_long_lines = [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - "cccccccccccccccccccccccccccccccccccccccc", - (1, 2, 3), - "dddddddddddddddddddddddddddddddddddddddd", -] -{ - "oneple": (1,), -} -{"oneple": (1,)} -["ls", "lsoneple/%s" % (foo,)] -x = {"oneple": (1,)} -y = { - "oneple": (1,), -} -assert False, ( - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wraps %s" - % bar -) - -# looping over a 1-tuple should also not get wrapped -for x in (1,): - pass -for (x,) in (1,), (2,), (3,): - pass - -[ - 1, - 2, - 3, -] - -division_result_tuple = (6 / 2,) -print("foo %r", (foo.bar,)) - -if True: - IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING = ( - Config.IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING - | {pylons.controllers.WSGIController} - ) - -if True: - ec2client.get_waiter("instance_stopped").wait( - InstanceIds=[instance.id], - WaiterConfig={ - "Delay": 5, - }, - ) - ec2client.get_waiter("instance_stopped").wait( - InstanceIds=[instance.id], - WaiterConfig={ - "Delay": 5, - }, - ) - ec2client.get_waiter("instance_stopped").wait( - InstanceIds=[instance.id], - WaiterConfig={ - "Delay": 5, - }, - ) -``` - - diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments2_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments2_py.snap index 8d6d0a4434..fb232c5523 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments2_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments2_py.snap @@ -178,7 +178,7 @@ instruction()#comment with bad spacing ```diff --- Black +++ Ruff -@@ -1,31 +1,31 @@ +@@ -1,8 +1,8 @@ from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( - MyLovelyCompanyTeamProjectComponent, # NOT DRY + MyLovelyCompanyTeamProjectComponent, @@ -189,52 +189,25 @@ instruction()#comment with bad spacing ) # Please keep __all__ alphabetized within each category. - - __all__ = [ - # Super-special typing primitives. -- "Any", -- "Callable", -- "ClassVar", -+ 'Any', -+ 'Callable', -+ 'ClassVar', +@@ -13,7 +13,7 @@ + "Callable", + "ClassVar", # ABCs (from collections.abc). - "AbstractSet", # collections.abc.Set. -- "ByteString", -- "Container", -+ 'AbstractSet', -+ 'ByteString', -+ 'Container', ++ "AbstractSet", + "ByteString", + "Container", # Concrete collection types. -- "Counter", -- "Deque", -- "Dict", -- "DefaultDict", -- "List", -- "Set", -- "FrozenSet", +@@ -24,7 +24,7 @@ + "List", + "Set", + "FrozenSet", - "NamedTuple", # Not really a type. -- "Generator", -+ 'Counter', -+ 'Deque', -+ 'Dict', -+ 'DefaultDict', -+ 'List', -+ 'Set', -+ 'FrozenSet', -+ 'NamedTuple', -+ 'Generator', ++ "NamedTuple", + "Generator", ] - not_shareables = [ -@@ -48,38 +48,45 @@ - SubBytes(b"spam"), - ] - --if "PYTHON" in os.environ: -+if 'PYTHON' in os.environ: - add_compiler(compiler_from_env()) - else: +@@ -54,32 +54,39 @@ # for compiler in compilers.values(): # add_compiler(compiler) add_compiler(compilers[(7.0, 32)]) @@ -284,7 +257,7 @@ instruction()#comment with bad spacing ): pass # no newline before or after -@@ -103,47 +110,47 @@ +@@ -103,42 +110,42 @@ ############################################################################ call2( @@ -323,12 +296,11 @@ instruction()#comment with bad spacing ] lcomp3 = [ # This one is actually too long to fit in a single line. -- element.split("\n", 1)[0] + element.split("\n", 1)[0] - # yup - for element in collection.select_elements() - # right - if element is not None -+ element.split('\n', 1)[0] + for # yup + element in collection.select_elements() + if # right @@ -346,12 +318,6 @@ instruction()#comment with bad spacing # let's return return Node( - syms.simple_stmt, -- [Node(statement, result), Leaf(token.NEWLINE, "\n")], # FIXME: \r\n? -+ [Node(statement, result), Leaf(token.NEWLINE, '\n')], # FIXME: \r\n? - ) - - @@ -167,7 +174,7 @@ ####################### @@ -377,23 +343,23 @@ from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component __all__ = [ # Super-special typing primitives. - 'Any', - 'Callable', - 'ClassVar', + "Any", + "Callable", + "ClassVar", # ABCs (from collections.abc). - 'AbstractSet', - 'ByteString', - 'Container', + "AbstractSet", + "ByteString", + "Container", # Concrete collection types. - 'Counter', - 'Deque', - 'Dict', - 'DefaultDict', - 'List', - 'Set', - 'FrozenSet', - 'NamedTuple', - 'Generator', + "Counter", + "Deque", + "Dict", + "DefaultDict", + "List", + "Set", + "FrozenSet", + "NamedTuple", + "Generator", ] not_shareables = [ @@ -416,7 +382,7 @@ not_shareables = [ SubBytes(b"spam"), ] -if 'PYTHON' in os.environ: +if "PYTHON" in os.environ: add_compiler(compiler_from_env()) else: # for compiler in compilers.values(): @@ -501,7 +467,7 @@ short ] lcomp3 = [ # This one is actually too long to fit in a single line. - element.split('\n', 1)[0] + element.split("\n", 1)[0] for # yup element in collection.select_elements() if # right @@ -518,7 +484,7 @@ short # let's return return Node( syms.simple_stmt, - [Node(statement, result), Leaf(token.NEWLINE, '\n')], # FIXME: \r\n? + [Node(statement, result), Leaf(token.NEWLINE, "\n")], # FIXME: \r\n? ) diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__empty_lines_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__empty_lines_py.snap index 64f776a402..e8442851d3 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__empty_lines_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__empty_lines_py.snap @@ -105,20 +105,7 @@ def g(): ```diff --- Black +++ Ruff -@@ -3,9 +3,9 @@ - - # leading comment - def f(): -- NO = "" -- SPACE = " " -- DOUBLESPACE = " " -+ NO = '' -+ SPACE = ' ' -+ DOUBLESPACE = ' ' - - t = leaf.type - p = leaf.parent # trailing comment -@@ -25,35 +25,41 @@ +@@ -25,23 +25,30 @@ return NO if prevp.type == token.EQUAL: @@ -164,21 +151,14 @@ def g(): return NO - ############################################################################### +@@ -49,7 +56,6 @@ # SECTION BECAUSE SECTIONS ############################################################################### + - - def g(): -- NO = "" -- SPACE = " " -- DOUBLESPACE = " " -+ NO = '' -+ SPACE = ' ' -+ DOUBLESPACE = ' ' - - t = leaf.type - p = leaf.parent + NO = "" + SPACE = " " @@ -67,7 +73,7 @@ return DOUBLESPACE @@ -221,9 +201,9 @@ def g(): # leading comment def f(): - NO = '' - SPACE = ' ' - DOUBLESPACE = ' ' + NO = "" + SPACE = " " + DOUBLESPACE = " " t = leaf.type p = leaf.parent # trailing comment @@ -275,9 +255,9 @@ def f(): ############################################################################### def g(): - NO = '' - SPACE = ' ' - DOUBLESPACE = ' ' + NO = "" + SPACE = " " + DOUBLESPACE = " " t = leaf.type p = leaf.parent diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap index 22949e554f..f42ec66248 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap @@ -268,22 +268,16 @@ last_call() --- Black +++ Ruff @@ -1,5 +1,6 @@ --"some_string" --b"\\xa3" +... -+'some_string' + "some_string" +-b"\\xa3" +b'\\xa3' Name None True -@@ -35,10 +36,11 @@ - lambda arg: None - lambda a=True: a - lambda a, b, c=True: a --lambda a, b, c=True, *, d=(1 << v2), e="str": a --lambda a, b, c=True, *vararg, d=(v1 << 2), e="str", **kwargs: a + b -+lambda a, b, c=True, *, d=(1 << v2), e='str': a -+lambda a, b, c=True, *vararg, d=(v1 << 2), e='str', **kwargs: a + b +@@ -38,7 +39,8 @@ + lambda a, b, c=True, *, d=(1 << v2), e="str": a + lambda a, b, c=True, *vararg, d=(v1 << 2), e="str", **kwargs: a + b manylambdas = lambda x=lambda y=lambda z=1: z: y(): x() -foo = lambda port_id, ignore_missing: { +foo = lambda port_id, @@ -291,38 +285,16 @@ last_call() "port1": port1_resource, "port2": port2_resource, }[port_id] -@@ -52,11 +54,11 @@ - if (1 if super_long_test_name else 2) - else (str or bytes or None) - ) --{"2.7": dead, "3.7": (long_live or die_hard)} --{"2.7": dead, "3.7": (long_live or die_hard), **{"3.6": verygood}} -+{'2.7': dead, '3.7': (long_live or die_hard)} -+{'2.7': dead, '3.7': (long_live or die_hard), **{'3.6': verygood}} +@@ -56,7 +58,7 @@ + {"2.7": dead, "3.7": (long_live or die_hard), **{"3.6": verygood}} {**a, **b, **c} --{"2.7", "3.6", "3.7", "3.8", "3.9", ("4.0" if gilectomy else "3.10")} + {"2.7", "3.6", "3.7", "3.8", "3.9", ("4.0" if gilectomy else "3.10")} -({"a": "b"}, (True or False), (+value), "string", b"bytes") or None -+{'2.7', '3.6', '3.7', '3.8', '3.9', ('4.0' if gilectomy else '3.10')} -+({'a': 'b'}, (True or False), (+value), 'string', b'bytes') or None ++({"a": "b"}, (True or False), (+value), "string", b'bytes') or None () (1,) (1, 2) -@@ -88,32 +90,33 @@ - ] - {i for i in (1, 2, 3)} - {(i**2) for i in (1, 2, 3)} --{(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))} -+{(i**2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))} - {((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)} - [i for i in (1, 2, 3)] - [(i**2) for i in (1, 2, 3)] --[(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))] -+[(i**2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))] - [((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)] - {i: 0 for i in (1, 2, 3)} --{i: j for i, j in ((1, "a"), (2, "b"), (3, "c"))} -+{i: j for i, j in ((1, 'a'), (2, 'b'), (3, 'c'))} - {a: b * 2 for a, b in dictionary.items()} +@@ -100,7 +102,8 @@ {a: b * -2 for a, b in dictionary.items()} { k: v @@ -332,23 +304,6 @@ last_call() } Python3 > Python2 > COBOL Life is Life - call() - call(arg) --call(kwarg="hey") --call(arg, kwarg="hey") --call(arg, another, kwarg="hey", **kwargs) -+call(kwarg='hey') -+call(arg, kwarg='hey') -+call(arg, another, kwarg='hey', **kwargs) - call( - this_is_a_very_long_variable_which_will_force_a_delimiter_split, - arg, - another, -- kwarg="hey", -+ kwarg='hey', - **kwargs, - ) # note: no trailing comma pre-3.6 - call(*gidgets[:2]) @@ -122,8 +125,8 @@ call(b, **self.screen_kwargs) lukasz.langa.pl @@ -367,19 +322,19 @@ last_call() -xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore - sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__) +xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = ( ++ classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) # type: ignore ++) ++xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = ( + classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) # type: ignore ) -xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore - sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__) +xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = ( -+ classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) # type: ignore ++ classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) ) -xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( - sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__) -) # type: ignore -+xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = ( -+ classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) -+) slice[0] slice[0:1] slice[0:1:2] @@ -405,46 +360,25 @@ last_call() numpy[:, (0, 1, 2, 5)] numpy[0, [0]] numpy[:, [i]] -@@ -172,17 +175,17 @@ +@@ -172,7 +175,7 @@ numpy[-(c + 1) :, d] numpy[:, l[-2]] numpy[:, ::-1] -numpy[np.newaxis, :] +numpy[np.newaxis, ::] (str or None) if (sys.version_info[0] > (3,)) else (str or bytes or None) --{"2.7": dead, "3.7": long_live or die_hard} --{"2.7", "3.6", "3.7", "3.8", "3.9", "4.0" if gilectomy else "3.10"} -+{'2.7': dead, '3.7': long_live or die_hard} -+{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'} - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C] - (SomeName) - SomeName - (Good, Bad, Ugly) - (i for i in (1, 2, 3)) - ((i**2) for i in (1, 2, 3)) --((i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))) -+((i**2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))) - (((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)) - (*starred,) - { + {"2.7": dead, "3.7": long_live or die_hard} + {"2.7", "3.6", "3.7", "3.8", "3.9", "4.0" if gilectomy else "3.10"} @@ -201,30 +204,26 @@ e = (1,).count(1) f = 1, *range(10) g = 1, *"ten" -what_is_up_with_those_new_coord_names = (coord_names + set(vars_to_create)) + set( - vars_to_remove -+what_is_up_with_those_new_coord_names = ( -+ (coord_names -+ + set(vars_to_create)) -+ + set(vars_to_remove) - ) +-) -what_is_up_with_those_new_coord_names = (coord_names | set(vars_to_create)) - set( - vars_to_remove -+what_is_up_with_those_new_coord_names = ( -+ (coord_names -+ | set(vars_to_create)) -+ - set(vars_to_remove) - ) +-) -result = ( - session.query(models.Customer.id) - .filter( @@ -452,7 +386,11 @@ last_call() - ) - .order_by(models.Customer.id.asc()) - .all() --) ++what_is_up_with_those_new_coord_names = ( ++ (coord_names ++ + set(vars_to_create)) ++ + set(vars_to_remove) + ) -result = ( - session.query(models.Customer.id) - .filter( @@ -462,7 +400,11 @@ last_call() - models.Customer.id.asc(), - ) - .all() --) ++what_is_up_with_those_new_coord_names = ( ++ (coord_names ++ | set(vars_to_create)) ++ - set(vars_to_remove) + ) +result = session.query(models.Customer.id).filter( + models.Customer.account_id == account_id, + models.Customer.email == email_address, @@ -489,15 +431,6 @@ last_call() assert parens is TooMany for (x,) in (1,), (2,), (3,): ... -@@ -272,7 +271,7 @@ - addr_proto, - addr_canonname, - addr_sockaddr, --) in socket.getaddrinfo("google.com", "http"): -+) in socket.getaddrinfo('google.com', 'http'): - pass - a = ( - aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp @@ -327,13 +326,18 @@ ): return True @@ -536,7 +469,7 @@ last_call() ```py ... -'some_string' +"some_string" b'\\xa3' Name None @@ -573,8 +506,8 @@ flags & ~select.EPOLLIN and waiters.write_task is not None lambda arg: None lambda a=True: a lambda a, b, c=True: a -lambda a, b, c=True, *, d=(1 << v2), e='str': a -lambda a, b, c=True, *vararg, d=(v1 << 2), e='str', **kwargs: a + b +lambda a, b, c=True, *, d=(1 << v2), e="str": a +lambda a, b, c=True, *vararg, d=(v1 << 2), e="str", **kwargs: a + b manylambdas = lambda x=lambda y=lambda z=1: z: y(): x() foo = lambda port_id, ignore_missing,: { @@ -591,11 +524,11 @@ str or None if (1 if True else 2) else str or bytes or None if (1 if super_long_test_name else 2) else (str or bytes or None) ) -{'2.7': dead, '3.7': (long_live or die_hard)} -{'2.7': dead, '3.7': (long_live or die_hard), **{'3.6': verygood}} +{"2.7": dead, "3.7": (long_live or die_hard)} +{"2.7": dead, "3.7": (long_live or die_hard), **{"3.6": verygood}} {**a, **b, **c} -{'2.7', '3.6', '3.7', '3.8', '3.9', ('4.0' if gilectomy else '3.10')} -({'a': 'b'}, (True or False), (+value), 'string', b'bytes') or None +{"2.7", "3.6", "3.7", "3.8", "3.9", ("4.0" if gilectomy else "3.10")} +({"a": "b"}, (True or False), (+value), "string", b'bytes') or None () (1,) (1, 2) @@ -627,14 +560,14 @@ str or None if (1 if True else 2) else str or bytes or None ] {i for i in (1, 2, 3)} {(i**2) for i in (1, 2, 3)} -{(i**2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))} +{(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))} {((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)} [i for i in (1, 2, 3)] [(i**2) for i in (1, 2, 3)] -[(i**2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))] +[(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))] [((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)] {i: 0 for i in (1, 2, 3)} -{i: j for i, j in ((1, 'a'), (2, 'b'), (3, 'c'))} +{i: j for i, j in ((1, "a"), (2, "b"), (3, "c"))} {a: b * 2 for a, b in dictionary.items()} {a: b * -2 for a, b in dictionary.items()} { @@ -646,14 +579,14 @@ Python3 > Python2 > COBOL Life is Life call() call(arg) -call(kwarg='hey') -call(arg, kwarg='hey') -call(arg, another, kwarg='hey', **kwargs) +call(kwarg="hey") +call(arg, kwarg="hey") +call(arg, another, kwarg="hey", **kwargs) call( this_is_a_very_long_variable_which_will_force_a_delimiter_split, arg, another, - kwarg='hey', + kwarg="hey", **kwargs, ) # note: no trailing comma pre-3.6 call(*gidgets[:2]) @@ -714,15 +647,15 @@ numpy[:, l[-2]] numpy[:, ::-1] numpy[np.newaxis, ::] (str or None) if (sys.version_info[0] > (3,)) else (str or bytes or None) -{'2.7': dead, '3.7': long_live or die_hard} -{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'} +{"2.7": dead, "3.7": long_live or die_hard} +{"2.7", "3.6", "3.7", "3.8", "3.9", "4.0" if gilectomy else "3.10"} [1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C] (SomeName) SomeName (Good, Bad, Ugly) (i for i in (1, 2, 3)) ((i**2) for i in (1, 2, 3)) -((i**2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))) +((i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))) (((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)) (*starred,) { @@ -808,7 +741,7 @@ for ( addr_proto, addr_canonname, addr_sockaddr, -) in socket.getaddrinfo('google.com', 'http'): +) in socket.getaddrinfo("google.com", "http"): pass a = ( aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff2_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff2_py.snap index 32bb882d20..dff3ec9ad8 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff2_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff2_py.snap @@ -67,11 +67,11 @@ def test_calculate_fades(): - - # Test don't manage the volume +@pytest.mark.parametrize( -+ 'test', ++ "test", [ - ('stuff', 'in') + # Test don't manage the volume -+ [('stuff', 'in')], ++ [("stuff", "in")], ], -]) +) @@ -123,10 +123,10 @@ TmEx = 2 # Position, Volume, State, TmSt/TmEx/None, [call, [arg1...]] @pytest.mark.parametrize( - 'test', + "test", [ # Test don't manage the volume - [('stuff', 'in')], + [("stuff", "in")], ], ) def test_fader(test): diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap index 1af351739c..171a290ea4 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap @@ -97,17 +97,20 @@ elif unformatted: ```diff --- Black +++ Ruff -@@ -5,8 +5,7 @@ +@@ -3,10 +3,8 @@ + entry_points={ + # fmt: off "console_scripts": [ - "foo-bar" - "=foo.bar.:main", +- "foo-bar" +- "=foo.bar.:main", - # fmt: on - ] # Includes an formatted indentation. ++ "foo-bar" "=foo.bar.:main", + ], }, ) -@@ -18,8 +17,8 @@ +@@ -18,8 +16,8 @@ "ls", "-la", ] @@ -118,7 +121,7 @@ elif unformatted: check=True, ) -@@ -27,9 +26,8 @@ +@@ -27,9 +25,8 @@ # Regression test for https://github.com/psf/black/issues/3026. def test_func(): # yapf: disable @@ -129,7 +132,7 @@ elif unformatted: elif b: return True -@@ -39,10 +37,10 @@ +@@ -39,10 +36,10 @@ # Regression test for https://github.com/psf/black/issues/2567. if True: # fmt: off @@ -144,7 +147,7 @@ elif unformatted: else: print("This will be formatted") -@@ -52,14 +50,11 @@ +@@ -52,14 +49,11 @@ async def call(param): if param: # fmt: off @@ -162,7 +165,7 @@ elif unformatted: print("This will be formatted") -@@ -68,20 +63,21 @@ +@@ -68,20 +62,21 @@ class Named(t.Protocol): # fmt: off @property @@ -198,8 +201,7 @@ setup( entry_points={ # fmt: off "console_scripts": [ - "foo-bar" - "=foo.bar.:main", + "foo-bar" "=foo.bar.:main", ], }, ) diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap index e7ad2eb55d..376701cf3f 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap @@ -117,32 +117,7 @@ def __await__(): return (yield) def func_no_args(): -@@ -27,7 +27,7 @@ - async def coroutine(arg, exec=False): - "Single-line docstring. Multiline is harder to reformat." - async with some_connection() as conn: -- await conn.do_what_i_mean("SELECT bobby, tables FROM xkcd", timeout=2) -+ await conn.do_what_i_mean('SELECT bobby, tables FROM xkcd', timeout=2) - await asyncio.sleep(1) - - -@@ -44,7 +44,7 @@ - return text[number:-1] - - --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(10000, 200000))) - assert task._cancel_stack[: len(old_stack)] == old_stack - -@@ -58,25 +58,20 @@ - f: int = -1, - g: int = 1 if False else 2, - h: str = "", -- i: str = r"", -+ i: str = r'', - ): - ... +@@ -64,19 +64,14 @@ def spaces2(result=_core.Value(None)): @@ -218,7 +193,7 @@ def func_no_args(): async def coroutine(arg, exec=False): "Single-line docstring. Multiline is harder to reformat." async with some_connection() as conn: - await conn.do_what_i_mean('SELECT bobby, tables FROM xkcd', timeout=2) + await conn.do_what_i_mean("SELECT bobby, tables FROM xkcd", timeout=2) await asyncio.sleep(1) @@ -235,7 +210,7 @@ def function_signature_stress_test( return text[number:-1] -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(10000, 200000))) assert task._cancel_stack[: len(old_stack)] == old_stack @@ -249,7 +224,7 @@ def spaces_types( f: int = -1, g: int = 1 if False else 2, h: str = "", - i: str = r'', + i: str = r"", ): ... diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_trailing_comma_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_trailing_comma_py.snap index 1ffca41f0d..b1b1d3dab3 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_trailing_comma_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_trailing_comma_py.snap @@ -74,35 +74,6 @@ some_module.some_function( ```diff --- Black +++ Ruff -@@ -2,7 +2,7 @@ - a, - ): - d = { -- "key": "value", -+ 'key': 'value', - } - tup = (1,) - -@@ -12,8 +12,8 @@ - b, - ): - d = { -- "key": "value", -- "key2": "value2", -+ 'key': 'value', -+ 'key2': 'value2', - } - tup = ( - 1, -@@ -26,7 +26,7 @@ - ): - call( - arg={ -- "explode": "this", -+ 'explode': 'this', - } - ) - call2( @@ -52,53 +52,52 @@ pass @@ -184,7 +155,7 @@ def f( a, ): d = { - 'key': 'value', + "key": "value", } tup = (1,) @@ -194,8 +165,8 @@ def f2( b, ): d = { - 'key': 'value', - 'key2': 'value2', + "key": "value", + "key2": "value2", } tup = ( 1, @@ -208,7 +179,7 @@ def f( ): call( arg={ - 'explode': 'this', + "explode": "this", } ) call2( diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__string_prefixes_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__string_prefixes_py.snap index 07e263b90e..702cbe4a2a 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__string_prefixes_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__string_prefixes_py.snap @@ -39,10 +39,9 @@ def docstring_multiline(): name = "Łukasz" -(f"hello {name}", f"hello {name}") -(b"", b"") --("", "") +(f"hello {name}", F"hello {name}") +(b"", B"") -+(u"", U"") + ("", "") (r"", R"") -(rf"", rf"", Rf"", Rf"", rf"", rf"", Rf"", Rf"") @@ -62,7 +61,7 @@ def docstring_multiline(): name = "Łukasz" (f"hello {name}", F"hello {name}") (b"", B"") -(u"", U"") +("", "") (r"", R"") (rf"", fr"", Rf"", fR"", rF"", Fr"", RF"", FR"") diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens1_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens1_py.snap index 47ea12c704..3511af0fc9 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens1_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens1_py.snap @@ -38,7 +38,7 @@ class A: ```diff --- Black +++ Ruff -@@ -1,15 +1,14 @@ +@@ -1,7 +1,6 @@ -if e1234123412341234.winerror not in ( - _winapi.ERROR_SEM_TIMEOUT, - _winapi.ERROR_PIPE_BUSY, @@ -49,23 +49,13 @@ class A: pass if x: - if y: - new_id = ( - max( -- Vegetable.objects.order_by("-id")[0].id, -- Mineral.objects.order_by("-id")[0].id, -+ Vegetable.objects.order_by('-id')[0].id, -+ Mineral.objects.order_by('-id')[0].id, - ) - + 1 - ) @@ -21,14 +20,20 @@ "Your password must contain at least %(min_length)d character.", "Your password must contain at least %(min_length)d characters.", self.min_length, - ) % {"min_length": self.min_length} + ) -+ % {'min_length': self.min_length} ++ % {"min_length": self.min_length} class A: @@ -100,8 +90,8 @@ if x: if y: new_id = ( max( - Vegetable.objects.order_by('-id')[0].id, - Mineral.objects.order_by('-id')[0].id, + Vegetable.objects.order_by("-id")[0].id, + Mineral.objects.order_by("-id")[0].id, ) + 1 ) @@ -114,7 +104,7 @@ class X: "Your password must contain at least %(min_length)d characters.", self.min_length, ) - % {'min_length': self.min_length} + % {"min_length": self.min_length} class A: diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens2_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens2_py.snap index 85eed38625..edc16d0dce 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens2_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens2_py.snap @@ -23,7 +23,7 @@ if (e123456.get_tk_patchlevel() >= (8, 6, 0, 'final') or - 8, -) <= get_tk_patchlevel() < (8, 6): +if ( -+ e123456.get_tk_patchlevel() >= (8, 6, 0, 'final') ++ e123456.get_tk_patchlevel() >= (8, 6, 0, "final") + or (8, 5, 8) <= get_tk_patchlevel() < (8, 6) +): pass @@ -33,7 +33,7 @@ if (e123456.get_tk_patchlevel() >= (8, 6, 0, 'final') or ```py if ( - e123456.get_tk_patchlevel() >= (8, 6, 0, 'final') + e123456.get_tk_patchlevel() >= (8, 6, 0, "final") or (8, 5, 8) <= get_tk_patchlevel() < (8, 6) ): pass