diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/list_comp.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/list_comp.py new file mode 100644 index 0000000000..4684d4dd0e --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/list_comp.py @@ -0,0 +1,32 @@ +[i for i in []] + +[i for i in [1,]] + +[ + a # a + for # for + c # c + in # in + e # e +] + +[ + # above a + a # a + # above for + for # for + # above c + c # c + # above in + in # in + # above e + e # e + # above if + if # if + # above f + f # f + # above if2 + if # if2 + # above g + g # g +] diff --git a/crates/ruff_python_formatter/src/comments/placement.rs b/crates/ruff_python_formatter/src/comments/placement.rs index bc8df853de..b82d4947c4 100644 --- a/crates/ruff_python_formatter/src/comments/placement.rs +++ b/crates/ruff_python_formatter/src/comments/placement.rs @@ -38,6 +38,7 @@ pub(super) fn place_comment<'a>( handle_slice_comments, handle_attribute_comment, handle_expr_if_comment, + handle_comprehension_comment, handle_trailing_expression_starred_star_end_of_line_comment, ]; for handler in HANDLERS { @@ -1244,6 +1245,137 @@ fn find_only_token_in_range(range: TextRange, locator: &Locator, token_kind: Tok token } +// Handle comments inside comprehensions, e.g. +// +// ```python +// [ +// a +// for # dangling on the comprehension +// b +// # dangling on the comprehension +// in # dangling on comprehension.iter +// # leading on the iter +// c +// # dangling on comprehension.if.n +// if # dangling on comprehension.if.n +// d +// ] +// ``` +fn handle_comprehension_comment<'a>( + comment: DecoratedComment<'a>, + locator: &Locator, +) -> CommentPlacement<'a> { + let AnyNodeRef::Comprehension(comprehension) = comment.enclosing_node() else { + return CommentPlacement::Default(comment); + }; + let is_own_line = comment.line_position().is_own_line(); + + // Comments between the `for` and target + // ```python + // [ + // a + // for # attache as dangling on the comprehension + // b in c + // ] + // ``` + if comment.slice().end() < comprehension.target.range().start() { + return if is_own_line { + // own line comments are correctly assigned as leading the target + CommentPlacement::Default(comment) + } else { + // after the `for` + CommentPlacement::dangling(comment.enclosing_node(), comment) + }; + } + + let in_token = find_only_token_in_range( + TextRange::new( + comprehension.target.range().end(), + comprehension.iter.range().start(), + ), + locator, + TokenKind::In, + ); + + // Comments between the target and the `in` + // ```python + // [ + // a for b + // # attach as dangling on the target + // # (to be rendered as leading on the "in") + // in c + // ] + // ``` + if comment.slice().start() < in_token.start() { + // attach as dangling comments on the target + // (to be rendered as leading on the "in") + return if is_own_line { + CommentPlacement::dangling(comment.enclosing_node(), comment) + } else { + // correctly trailing on the target + CommentPlacement::Default(comment) + }; + } + + // Comments between the `in` and the iter + // ```python + // [ + // a for b + // in # attach as dangling on the iter + // c + // ] + // ``` + if comment.slice().start() < comprehension.iter.range().start() { + return if is_own_line { + CommentPlacement::Default(comment) + } else { + // after the `in` but same line, turn into trailing on the `in` token + CommentPlacement::dangling((&comprehension.iter).into(), comment) + }; + } + + let mut last_end = comprehension.iter.range().end(); + + for if_node in &comprehension.ifs { + // ```python + // [ + // a + // for + // c + // in + // e + // # above if <-- find these own-line between previous and `if` token + // if # if <-- find these end-of-line between `if` and if node (`f`) + // # above f <-- already correctly assigned as leading `f` + // f # f <-- already correctly assigned as trailing `f` + // # above if2 + // if # if2 + // # above g + // g # g + // ] + // ``` + let if_token = find_only_token_in_range( + TextRange::new(last_end, if_node.range().start()), + locator, + TokenKind::If, + ); + if is_own_line { + if last_end < comment.slice().start() && comment.slice().start() < if_token.start() { + return CommentPlacement::dangling((if_node).into(), comment); + } + } else { + if if_token.start() < comment.slice().start() + && comment.slice().start() < if_node.range().start() + { + return CommentPlacement::dangling((if_node).into(), comment); + } + } + last_end = if_node.range().end(); + } + + CommentPlacement::Default(comment) +} + /// Returns `true` if `right` is `Some` and `left` and `right` are referentially equal. fn are_same_optional<'a, T>(left: AnyNodeRef, right: Option) -> bool where diff --git a/crates/ruff_python_formatter/src/expression/expr_list_comp.rs b/crates/ruff_python_formatter/src/expression/expr_list_comp.rs index 5bc6a3017a..a6d5e236fd 100644 --- a/crates/ruff_python_formatter/src/expression/expr_list_comp.rs +++ b/crates/ruff_python_formatter/src/expression/expr_list_comp.rs @@ -1,20 +1,41 @@ use crate::comments::Comments; use crate::expression::parentheses::{ - default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, + default_expression_needs_parentheses, parenthesized, NeedsParentheses, Parentheses, + Parenthesize, }; -use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter}; -use ruff_formatter::{write, Buffer, FormatResult}; +use crate::prelude::*; +use crate::AsFormat; +use crate::{FormatNodeRule, PyFormatter}; +use ruff_formatter::{format_args, write, Buffer, FormatResult}; use rustpython_parser::ast::ExprListComp; #[derive(Default)] pub struct FormatExprListComp; impl FormatNodeRule for FormatExprListComp { - fn fmt_fields(&self, _item: &ExprListComp, f: &mut PyFormatter) -> FormatResult<()> { + fn fmt_fields(&self, item: &ExprListComp, f: &mut PyFormatter) -> FormatResult<()> { + let ExprListComp { + range: _, + elt, + generators, + } = item; + + let joined = format_with(|f| { + f.join_with(soft_line_break_or_space()) + .entries(generators.iter().formatted()) + .finish() + }); + write!( f, - [not_yet_implemented_custom_text( - "[NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []]" + [parenthesized( + "[", + &format_args!( + group(&elt.format()), + soft_line_break_or_space(), + group(&joined) + ), + "]" )] ) } diff --git a/crates/ruff_python_formatter/src/other/comprehension.rs b/crates/ruff_python_formatter/src/other/comprehension.rs index edf64d8bee..022c5b82e6 100644 --- a/crates/ruff_python_formatter/src/other/comprehension.rs +++ b/crates/ruff_python_formatter/src/other/comprehension.rs @@ -1,12 +1,87 @@ -use crate::{not_yet_implemented, FormatNodeRule, PyFormatter}; -use ruff_formatter::{write, Buffer, FormatResult}; -use rustpython_parser::ast::Comprehension; +use crate::comments::{leading_comments, trailing_comments}; +use crate::prelude::*; +use crate::AsFormat; +use crate::{FormatNodeRule, PyFormatter}; +use ruff_formatter::{format_args, write, Buffer, FormatResult}; +use rustpython_parser::ast::{Comprehension, Ranged}; #[derive(Default)] pub struct FormatComprehension; impl FormatNodeRule for FormatComprehension { fn fmt_fields(&self, item: &Comprehension, f: &mut PyFormatter) -> FormatResult<()> { - write!(f, [not_yet_implemented(item)]) + let Comprehension { + range: _, + target, + iter, + ifs, + is_async, + } = item; + + let comments = f.context().comments().clone(); + + if *is_async { + write!(f, [text("async"), space()])?; + } + + let dangling_item_comments = comments.dangling_comments(item); + + let (before_target_comments, before_in_comments) = dangling_item_comments.split_at( + dangling_item_comments + .partition_point(|comment| comment.slice().end() < target.range().start()), + ); + + let trailing_in_comments = comments.dangling_comments(iter); + write!( + f, + [ + text("for"), + trailing_comments(before_target_comments), + group(&format_args!( + soft_line_break_or_space(), + target.format(), + soft_line_break_or_space(), + leading_comments(before_in_comments), + text("in"), + trailing_comments(trailing_in_comments), + soft_line_break_or_space(), + iter.format(), + )), + ] + )?; + if !ifs.is_empty() { + let joined = format_with(|f| { + let mut joiner = f.join_with(soft_line_break_or_space()); + for if_case in ifs { + let dangling_if_comments = comments.dangling_comments(if_case); + + let (own_line_if_comments, end_of_line_if_comments) = dangling_if_comments + .split_at( + dangling_if_comments + .partition_point(|comment| comment.line_position().is_own_line()), + ); + joiner.entry(&group(&format_args!( + leading_comments(own_line_if_comments), + text("if"), + trailing_comments(end_of_line_if_comments), + soft_line_break_or_space(), + if_case.format(), + ))); + } + joiner.finish() + }); + + write!(f, [soft_line_break_or_space(), group(&joined)])?; + } + Ok(()) + } + + fn fmt_dangling_comments( + &self, + _node: &Comprehension, + _f: &mut PyFormatter, + ) -> FormatResult<()> { + // dangling comments are formatted as part of fmt_fields + Ok(()) } } diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_37__python37.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_37__python37.py.snap index 785d395e09..8af05b54ea 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_37__python37.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_37__python37.py.snap @@ -42,7 +42,7 @@ def make_arange(n): ```diff --- Black +++ Ruff -@@ -2,29 +2,21 @@ +@@ -2,29 +2,27 @@ def f(): @@ -60,13 +60,16 @@ def make_arange(n): async def func(): if test: -- out_batched = [ -- i + out_batched = [ + i - async for i in aitertools._async_map( - self.async_inc, arange(8), batch_size=3 - ) -- ] -+ out_batched = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] ++ async for ++ i ++ in ++ aitertools._async_map(self.async_inc, arange(8), batch_size=3) + ] def awaited_generator_value(n): @@ -95,7 +98,13 @@ def g(): async def func(): if test: - out_batched = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] + out_batched = [ + i + async for + i + in + aitertools._async_map(self.async_inc, arange(8), batch_size=3) + ] def awaited_generator_value(n): diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_38__pep_572.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_38__pep_572.py.snap index f64c729232..62a7d893b3 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_38__pep_572.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_38__pep_572.py.snap @@ -59,7 +59,7 @@ while x := f(x): ```diff --- Black +++ Ruff -@@ -2,10 +2,10 @@ +@@ -2,7 +2,7 @@ (a := a) if (match := pattern.search(data)) is None: pass @@ -67,11 +67,7 @@ while x := f(x): +if (match := pattern.search(data)): pass [y := f(x), y**2, y**3] --filtered_data = [y for x in data if (y := f(x)) is None] -+filtered_data = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] - (y := f(x)) - y0 = (y1 := f(x)) - foo(x=(y := f(x))) + filtered_data = [y for x in data if (y := f(x)) is None] @@ -19,10 +19,10 @@ pass @@ -122,7 +118,7 @@ if (match := pattern.search(data)) is None: if (match := pattern.search(data)): pass [y := f(x), y**2, y**3] -filtered_data = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] +filtered_data = [y for x in data if (y := f(x)) is None] (y := f(x)) y0 = (y1 := f(x)) foo(x=(y := f(x))) diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments2.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments2.py.snap index e7ae03c930..ef5229fdb1 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments2.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments2.py.snap @@ -224,36 +224,43 @@ instruction()#comment with bad spacing if ( self._proc is not None # has the child process finished? -@@ -114,25 +122,9 @@ - # yup +@@ -115,7 +123,12 @@ arg3=True, ) -- lcomp = [ + lcomp = [ - element for element in collection if element is not None # yup # yup # right -- ] -- lcomp2 = [ -- # hello -- element -- # yup -- for element in collection -- # right ++ element # yup ++ for ++ element ++ in ++ collection # yup ++ if element is not None # right + ] + lcomp2 = [ + # hello +@@ -123,7 +136,9 @@ + # yup + for element in collection + # right - if element is not None -- ] -- 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 + ] + lcomp3 = [ + # This one is actually too long to fit in a single line. +@@ -131,7 +146,9 @@ + # yup + for element in collection.select_elements() + # right - if element is not None -- ] -+ lcomp = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] -+ lcomp2 = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] -+ lcomp3 = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] ++ if ++ element ++ is not None + ] while True: if False: - continue -@@ -143,7 +135,10 @@ +@@ -143,7 +160,10 @@ # let's return return Node( syms.simple_stmt, @@ -265,7 +272,7 @@ instruction()#comment with bad spacing ) -@@ -158,7 +153,11 @@ +@@ -158,7 +178,11 @@ class Test: def _init_host(self, parsed) -> None: @@ -407,9 +414,34 @@ short # yup arg3=True, ) - lcomp = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] - lcomp2 = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] - lcomp3 = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] + lcomp = [ + element # yup + for + element + in + collection # yup + if element is not None # right + ] + lcomp2 = [ + # hello + element + # yup + for element in collection + # right + if + element + is not None + ] + 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 + ] while True: if False: continue diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments3.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments3.py.snap index 2812b9d37a..d41e20b68f 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments3.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments3.py.snap @@ -60,22 +60,17 @@ def func(): ```diff --- Black +++ Ruff -@@ -6,14 +6,7 @@ - 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 +@@ -12,7 +12,9 @@ + # yup + for element in collection.select_elements() + # right - if element is not None -- ] -+ lcomp3 = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] ++ 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 = [] ``` ## Ruff Output @@ -89,7 +84,16 @@ def func(): x = """ a really long string """ - lcomp3 = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] + 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 = [] diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap index b8aff61aa7..8fc2eb367f 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap @@ -324,10 +324,14 @@ last_call() -{(i**2) for i in (1, 2, 3)} -{(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) + j) for i in (1, 2, 3) for j in (1, 2, 3)] ++{NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set} ++{NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set} ++{NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set} ++{NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set} + [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) + 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"))} -{a: b * 2 for a, b in dictionary.items()} @@ -336,14 +340,6 @@ last_call() - k: v - for k, v in this_is_a_very_long_variable_which_will_cause_a_trailing_comma_which_breaks_the_comprehension -} -+{NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set} -+{NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set} -+{NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set} -+{NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set} -+[NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] -+[NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] -+[NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] -+[NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] +{NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} +{NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} +{NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} @@ -369,9 +365,6 @@ last_call() - int, - float, - dict[str, int], --] --very_long_variable_name_filters: t.List[ -- t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]], + ( + str, + int, @@ -379,6 +372,9 @@ last_call() + dict[str, int], + ) ] +-very_long_variable_name_filters: t.List[ +- t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]], +-] -xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore - sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__) -) @@ -433,10 +429,11 @@ last_call() (*starred,) { "id": "1", -@@ -208,24 +202,14 @@ +@@ -207,25 +201,15 @@ + ) 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( @@ -444,7 +441,7 @@ last_call() - ) - .order_by(models.Customer.id.asc()) - .all() --) + ) -result = ( - session.query(models.Customer.id) - .filter( @@ -645,10 +642,10 @@ str or None if (1 if True else 2) else str or bytes or None {NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set} {NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set} {NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set} -[NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] -[NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] -[NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] -[NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] +[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) + j) for i in (1, 2, 3) for j in (1, 2, 3)] {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__power_op_spacing.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__power_op_spacing.py.snap index 40cab8fa6b..d315462fd0 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__power_op_spacing.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__power_op_spacing.py.snap @@ -75,7 +75,7 @@ return np.divide( ```diff --- Black +++ Ruff -@@ -15,38 +15,38 @@ +@@ -15,7 +15,7 @@ b = 5 ** f() c = -(5**2) d = 5 ** f["hi"] @@ -84,21 +84,16 @@ return np.divide( f = f() ** 5 g = a.b**c.d h = 5 ** funcs.f() - i = funcs.f() ** 5 - j = super().name ** 5 --k = [(2**idx, value) for idx, value in pairs] -+k = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] - l = mod.weights_[0] == pytest.approx(0.95**100, abs=0.001) +@@ -26,7 +26,7 @@ m = [([2**63], [1, 2**63])] n = count <= 10**5 o = settings(max_examples=10**6) -p = {(k, k**2): v**2 for k, v in pairs} --q = [10**i for i in range(6)] +p = {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} -+q = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] + q = [10**i for i in range(6)] r = x**y - a = 5.0**~4.0 +@@ -34,7 +34,7 @@ b = 5.0 ** f() c = -(5.0**2.0) d = 5.0 ** f["hi"] @@ -107,21 +102,15 @@ return np.divide( f = f() ** 5.0 g = a.b**c.d h = 5.0 ** funcs.f() - i = funcs.f() ** 5.0 - j = super().name ** 5.0 --k = [(2.0**idx, value) for idx, value in pairs] -+k = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] - l = mod.weights_[0] == pytest.approx(0.95**100, abs=0.001) +@@ -45,7 +45,7 @@ m = [([2.0**63.0], [1.0, 2**63.0])] n = count <= 10**5.0 o = settings(max_examples=10**6.0) -p = {(k, k**2): v**2.0 for k, v in pairs} --q = [10.5**i for i in range(6)] +p = {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} -+q = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] + q = [10.5**i for i in range(6)] - # WE SHOULD DEFINITELY NOT EAT THESE COMMENTS (https://github.com/psf/black/issues/2873) @@ -55,9 +55,11 @@ view.variance, # type: ignore[union-attr] view.sum_of_weights, # type: ignore[union-attr] @@ -164,13 +153,13 @@ g = a.b**c.d h = 5 ** funcs.f() i = funcs.f() ** 5 j = super().name ** 5 -k = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] +k = [(2**idx, value) for idx, value in pairs] l = mod.weights_[0] == pytest.approx(0.95**100, abs=0.001) m = [([2**63], [1, 2**63])] n = count <= 10**5 o = settings(max_examples=10**6) p = {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} -q = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] +q = [10**i for i in range(6)] r = x**y a = 5.0**~4.0 @@ -183,13 +172,13 @@ g = a.b**c.d h = 5.0 ** funcs.f() i = funcs.f() ** 5.0 j = super().name ** 5.0 -k = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] +k = [(2.0**idx, value) for idx, value in pairs] l = mod.weights_[0] == pytest.approx(0.95**100, abs=0.001) m = [([2.0**63.0], [1.0, 2**63.0])] n = count <= 10**5.0 o = settings(max_examples=10**6.0) p = {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} -q = [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] +q = [10.5**i for i in range(6)] # WE SHOULD DEFINITELY NOT EAT THESE COMMENTS (https://github.com/psf/black/issues/2873) diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__slices.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__slices.py.snap index cfa0bebf04..848f65c25e 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__slices.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__slices.py.snap @@ -43,7 +43,7 @@ ham[lower + offset : upper + offset] ```diff --- Black +++ Ruff -@@ -4,28 +4,34 @@ +@@ -4,19 +4,21 @@ slice[d::d] slice[0] slice[-1] @@ -68,19 +68,11 @@ ham[lower + offset : upper + offset] +slice[ + (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) : x +] -+slice[ -+ :: [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] -+] ++slice[ :: [i for i in range(42)]] async def f(): -- slice[await x : [i async for i in arange(42)] : 42] -+ slice[ -+ await x : [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] : 42 -+ ] - - - # These are from PEP-8: +@@ -27,5 +29,5 @@ ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] ham[lower:upper], ham[lower:upper:], ham[lower::step] # ham[lower+offset : upper+offset] @@ -112,15 +104,11 @@ slice[not so_simple : 1 < val <= 10] slice[ (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) : x ] -slice[ - :: [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] -] +slice[ :: [i for i in range(42)]] async def f(): - slice[ - await x : [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] : 42 - ] + slice[await x : [i async for i in arange(42)] : 42] # These are from PEP-8: diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap index 40a69332f5..890bfd41cb 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap @@ -277,10 +277,10 @@ aaaaaaaaaaaaaa + { dddddddddddddddd, eeeeeee, } -( - aaaaaaaaaaaaaa - + [NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []] -) +aaaaaaaaaaaaaa + [ + a + for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +] ( aaaaaaaaaaaaaa + (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__list_comp.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__list_comp.py.snap new file mode 100644 index 0000000000..36ef7fa6eb --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__list_comp.py.snap @@ -0,0 +1,86 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/list_comp.py +--- +## Input +```py +[i for i in []] + +[i for i in [1,]] + +[ + a # a + for # for + c # c + in # in + e # e +] + +[ + # above a + a # a + # above for + for # for + # above c + c # c + # above in + in # in + # above e + e # e + # above if + if # if + # above f + f # f + # above if2 + if # if2 + # above g + g # g +] +``` + +## Output +```py +[i for i in []] + +[ + i + for + i + in + [ + 1, + ] +] + +[ + a # a + for # for + c # c + in # in + e # e +] + +[ + # above a + a # a + # above for + for # for + # above c + c # c + # above in + in # in + # above e + e # e + # above if + if # if + # above f + f # f + # above if2 + if # if2 + # above g + g # g +] +``` + + +