diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/dict_comp.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/dict_comp.py new file mode 100644 index 0000000000..d4c68f01a5 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/dict_comp.py @@ -0,0 +1,45 @@ +{i: i for i in []} + +{i: i for i in [1,]} + +{ + a: a # a + for # for + c # c + in # in + e # e +} + +{ + # above a + 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 +} + +{ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + [dddddddddddddddddd, eeeeeeeeeeeeeeeeeee]: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + for + ccccccccccccccccccccccccccccccccccccccc, + ddddddddddddddddddd, [eeeeeeeeeeeeeeeeeeeeee, fffffffffffffffffffffffff] + in + eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffggggggggggggggggggggghhhhhhhhhhhhhhothermoreeand_even_moreddddddddddddddddddddd + if + fffffffffffffffffffffffffffffffffffffffffff < gggggggggggggggggggggggggggggggggggggggggggggg < hhhhhhhhhhhhhhhhhhhhhhhhhh + if + gggggggggggggggggggggggggggggggggggggggggggg +} diff --git a/crates/ruff_python_formatter/src/expression/expr_dict_comp.rs b/crates/ruff_python_formatter/src/expression/expr_dict_comp.rs index 322dfea898..cc3b48569f 100644 --- a/crates/ruff_python_formatter/src/expression/expr_dict_comp.rs +++ b/crates/ruff_python_formatter/src/expression/expr_dict_comp.rs @@ -1,6 +1,10 @@ use crate::context::PyFormatContext; -use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; -use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter}; +use crate::expression::parentheses::{parenthesized, NeedsParentheses, OptionalParentheses}; +use crate::AsFormat; +use crate::{FormatNodeRule, FormattedIterExt, PyFormatter}; +use ruff_formatter::prelude::{ + format_args, format_with, group, soft_line_break_or_space, space, text, +}; use ruff_formatter::{write, Buffer, FormatResult}; use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprDictComp; @@ -9,11 +13,33 @@ use rustpython_parser::ast::ExprDictComp; pub struct FormatExprDictComp; impl FormatNodeRule for FormatExprDictComp { - fn fmt_fields(&self, _item: &ExprDictComp, f: &mut PyFormatter) -> FormatResult<()> { + fn fmt_fields(&self, item: &ExprDictComp, f: &mut PyFormatter) -> FormatResult<()> { + let ExprDictComp { + range: _, + key, + value, + 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_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict}" + [parenthesized( + "{", + &format_args!( + group(&key.format()), + text(":"), + space(), + value.format(), + soft_line_break_or_space(), + group(&joined) + ), + "}" )] ) } 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 2f6786e0e7..b56a8edfee 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 @@ -316,27 +316,19 @@ last_call() () (1,) (1, 2) -@@ -95,14 +98,11 @@ - [(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()} --{a: b * -2 for a, b in dictionary.items()} --{ -- k: v +@@ -101,7 +104,10 @@ + {a: b * -2 for a, b in dictionary.items()} + { + k: v - for k, v in this_is_a_very_long_variable_which_will_cause_a_trailing_comma_which_breaks_the_comprehension --} -+{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} -+{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} ++ for ( ++ k, ++ v, ++ ) in this_is_a_very_long_variable_which_will_cause_a_trailing_comma_which_breaks_the_comprehension + } Python3 > Python2 > COBOL Life is Life - call() -@@ -115,7 +115,7 @@ +@@ -115,7 +121,7 @@ arg, another, kwarg="hey", @@ -345,7 +337,7 @@ last_call() ) # note: no trailing comma pre-3.6 call(*gidgets[:2]) call(a, *gidgets[:2]) -@@ -152,13 +152,13 @@ +@@ -152,13 +158,13 @@ slice[0:1] slice[0:1:2] slice[:] @@ -362,7 +354,7 @@ last_call() numpy[0, :] numpy[:, i] numpy[0, :2] -@@ -172,7 +172,7 @@ +@@ -172,7 +178,7 @@ numpy[1 : c + 1, c] numpy[-(c + 1) :, d] numpy[:, l[-2]] @@ -371,7 +363,7 @@ last_call() 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} -@@ -181,10 +181,10 @@ +@@ -181,10 +187,10 @@ (SomeName) SomeName (Good, Bad, Ugly) @@ -386,7 +378,7 @@ last_call() (*starred,) { "id": "1", -@@ -208,24 +208,14 @@ +@@ -208,24 +214,14 @@ what_is_up_with_those_new_coord_names = (coord_names | set(vars_to_create)) - set( vars_to_remove ) @@ -419,7 +411,7 @@ last_call() Ø = set() authors.łukasz.say_thanks() mapping = { -@@ -237,10 +227,10 @@ +@@ -237,10 +233,10 @@ def gen(): @@ -434,22 +426,16 @@ last_call() async def f(): -@@ -248,8 +238,12 @@ - +@@ -249,7 +245,7 @@ print(*[] or [1]) --print(**{1: 3} if False else {x: x for x in range(3)}) + print(**{1: 3} if False else {x: x for x in range(3)}) -print(*lambda x: x) -+print( -+ **{1: 3} -+ if False -+ else {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} -+) +print(*lambda NOT_YET_IMPLEMENTED_lambda: True) assert not Test, "Short message" assert this is ComplexTest and not requirements.fit_in_a_single_line( force=False -@@ -259,7 +253,7 @@ +@@ -259,7 +255,7 @@ ... for y in (): ... @@ -458,7 +444,7 @@ last_call() ... for i in call(): ... -@@ -328,13 +322,18 @@ +@@ -328,13 +324,18 @@ ): return True if ( @@ -480,7 +466,7 @@ last_call() ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l**aaaaaaaa.m // aaaaaaaa.n ): return True -@@ -342,7 +341,8 @@ +@@ -342,7 +343,8 @@ ~aaaaaaaaaaaaaaaa.a + aaaaaaaaaaaaaaaa.b - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e @@ -595,11 +581,17 @@ str or None if (1 if True else 2) else str or bytes or None [(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} -{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} +{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()} +{a: b * -2 for a, b in dictionary.items()} +{ + k: v + for ( + k, + v, + ) in this_is_a_very_long_variable_which_will_cause_a_trailing_comma_which_breaks_the_comprehension +} Python3 > Python2 > COBOL Life is Life call() @@ -735,11 +727,7 @@ async def f(): print(*[] or [1]) -print( - **{1: 3} - if False - else {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} -) +print(**{1: 3} if False else {x: x for x in range(3)}) print(*lambda NOT_YET_IMPLEMENTED_lambda: True) assert not Test, "Short message" assert this is ComplexTest and not requirements.fit_in_a_single_line( 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 846734242d..2793e97310 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 @@ -84,15 +84,6 @@ return np.divide( f = f() ** 5 g = a.b**c.d h = 5 ** funcs.f() -@@ -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} -+p = {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} - q = [10**i for i in range(6)] - r = x**y - @@ -34,7 +34,7 @@ b = 5.0 ** f() c = -(5.0**2.0) @@ -102,15 +93,6 @@ return np.divide( f = f() ** 5.0 g = a.b**c.d h = 5.0 ** funcs.f() -@@ -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} -+p = {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} - q = [10.5**i for i in range(6)] - - ``` ## Ruff Output @@ -144,7 +126,7 @@ 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} +p = {(k, k**2): v**2 for k, v in pairs} q = [10**i for i in range(6)] r = x**y @@ -163,7 +145,7 @@ 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} +p = {(k, k**2): v**2.0 for k, v in pairs} q = [10.5**i for i in range(6)] diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_parens.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_parens.py.snap index b7afd62f41..fa89bfa512 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_parens.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_parens.py.snap @@ -96,44 +96,6 @@ def example8(): def example4(): -@@ -50,35 +44,11 @@ - - - def example6(): -- return {a: a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]} -+ return {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} - - - def example7(): -- return { -- a: a -- for a in [ -- 1, -- 2, -- 3, -- 4, -- 5, -- 6, -- 7, -- 8, -- 9, -- 10, -- 11, -- 12, -- 13, -- 14, -- 15, -- 16, -- 17, -- 18, -- 19, -- 20000000000000000000, -- ] -- } -+ return {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} - - - def example8(): ``` ## Ruff Output @@ -185,11 +147,35 @@ def example5(): def example6(): - return {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} + return {a: a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]} def example7(): - return {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} + return { + a: a + for a in [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20000000000000000000, + ] + } def example8(): diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__dict_comp.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__dict_comp.py.snap new file mode 100644 index 0000000000..6a96be6a10 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__dict_comp.py.snap @@ -0,0 +1,111 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/dict_comp.py +--- +## Input +```py +{i: i for i in []} + +{i: i for i in [1,]} + +{ + a: a # a + for # for + c # c + in # in + e # e +} + +{ + # above a + 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 +} + +{ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + [dddddddddddddddddd, eeeeeeeeeeeeeeeeeee]: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + for + ccccccccccccccccccccccccccccccccccccccc, + ddddddddddddddddddd, [eeeeeeeeeeeeeeeeeeeeee, fffffffffffffffffffffffff] + in + eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffggggggggggggggggggggghhhhhhhhhhhhhhothermoreeand_even_moreddddddddddddddddddddd + if + fffffffffffffffffffffffffffffffffffffffffff < gggggggggggggggggggggggggggggggggggggggggggggg < hhhhhhhhhhhhhhhhhhhhhhhhhh + if + gggggggggggggggggggggggggggggggggggggggggggg +} +``` + +## Output +```py +{i: i for i in []} + +{ + i: i + for i in [ + 1, + ] +} + +{ + a: a # a + for c in e # for # c # in # e +} + +{ + # above a + 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 +} + +{ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + + [ + dddddddddddddddddd, + eeeeeeeeeeeeeeeeeee, + ]: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + for ( + ccccccccccccccccccccccccccccccccccccccc, + ddddddddddddddddddd, + [eeeeeeeeeeeeeeeeeeeeee, fffffffffffffffffffffffff], + ) in eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffggggggggggggggggggggghhhhhhhhhhhhhhothermoreeand_even_moreddddddddddddddddddddd + if fffffffffffffffffffffffffffffffffffffffffff + < gggggggggggggggggggggggggggggggggggggggggggggg + < hhhhhhhhhhhhhhhhhhhhhhhhhh + if gggggggggggggggggggggggggggggggggggggggggggg +} +``` + + +