diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/generator_exp.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/generator_exp.py new file mode 100644 index 0000000000..f586dcd7fb --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/generator_exp.py @@ -0,0 +1,27 @@ +(a for b in c) + +# parens around generator expression not required +len(a for b in c) + +# parens around generator expression required +sum((a for b in c), start=0) + +# black keeps these atm, but intends to remove them in the future: +# https://github.com/psf/black/issues/2943 +f((1 for _ in a)) + +# make sure source parenthesis detection isn't fooled by these +f((1) for _ in (a)) + +# combination of the two above +f(((1) for _ in (a))) + + +# black keeps these atm, but intends to remove them in the future: +# https://github.com/psf/black/issues/2943 +len( + ( # leading + a for b in c + # trailing + ) +) diff --git a/crates/ruff_python_formatter/src/expression/expr_call.rs b/crates/ruff_python_formatter/src/expression/expr_call.rs index 4054208baf..85475f4b9b 100644 --- a/crates/ruff_python_formatter/src/expression/expr_call.rs +++ b/crates/ruff_python_formatter/src/expression/expr_call.rs @@ -6,6 +6,7 @@ use ruff_python_ast::node::AnyNodeRef; use ruff_python_whitespace::{SimpleTokenizer, TokenKind}; use crate::comments::dangling_comments; +use crate::expression::expr_generator_exp::GeneratorExpParentheses; use crate::expression::parentheses::{ parenthesized, NeedsParentheses, OptionalParentheses, Parentheses, }; @@ -50,13 +51,23 @@ impl FormatNodeRule for FormatExprCall { let mut joiner = f.join_comma_separated(item.end()); match args.as_slice() { [argument] if keywords.is_empty() => { - let parentheses = - if is_single_argument_parenthesized(argument, item.end(), source) { - Parentheses::Always - } else { - Parentheses::Never - }; - joiner.entry(argument, &argument.format().with_options(parentheses)); + match argument { + Expr::GeneratorExp(generator_exp) => joiner.entry( + generator_exp, + &generator_exp + .format() + .with_options(GeneratorExpParentheses::StripIfOnlyFunctionArg), + ), + other => { + let parentheses = + if is_single_argument_parenthesized(argument, item.end(), source) { + Parentheses::Always + } else { + Parentheses::Never + }; + joiner.entry(other, &other.format().with_options(parentheses)) + } + }; } arguments => { joiner diff --git a/crates/ruff_python_formatter/src/expression/expr_generator_exp.rs b/crates/ruff_python_formatter/src/expression/expr_generator_exp.rs index b9e8b2ed65..5b93663fb0 100644 --- a/crates/ruff_python_formatter/src/expression/expr_generator_exp.rs +++ b/crates/ruff_python_formatter/src/expression/expr_generator_exp.rs @@ -1,21 +1,76 @@ use crate::context::PyFormatContext; +use crate::expression::parentheses::parenthesized; use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; -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, FormatRuleWithOptions}; use ruff_python_ast::node::AnyNodeRef; use rustpython_parser::ast::ExprGeneratorExp; +#[derive(Eq, PartialEq, Debug, Default)] +pub enum GeneratorExpParentheses { + #[default] + Default, + + // skip parens if the generator exp is the only argument to a function, e.g. + // ```python + // all(x for y in z)` + // ``` + StripIfOnlyFunctionArg, +} + +impl FormatRuleWithOptions> for FormatExprGeneratorExp { + type Options = GeneratorExpParentheses; + + fn with_options(mut self, options: Self::Options) -> Self { + self.parentheses = options; + self + } +} + #[derive(Default)] -pub struct FormatExprGeneratorExp; +pub struct FormatExprGeneratorExp { + parentheses: GeneratorExpParentheses, +} impl FormatNodeRule for FormatExprGeneratorExp { - fn fmt_fields(&self, _item: &ExprGeneratorExp, f: &mut PyFormatter) -> FormatResult<()> { - write!( - f, - [not_yet_implemented_custom_text( - "(NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in [])" - )] - ) + fn fmt_fields(&self, item: &ExprGeneratorExp, f: &mut PyFormatter) -> FormatResult<()> { + let ExprGeneratorExp { + range: _, + elt, + generators, + } = item; + + let joined = format_with(|f| { + f.join_with(soft_line_break_or_space()) + .entries(generators.iter().formatted()) + .finish() + }); + + if self.parentheses == GeneratorExpParentheses::StripIfOnlyFunctionArg { + write!( + f, + [ + group(&elt.format()), + soft_line_break_or_space(), + group(&joined), + ] + ) + } else { + write!( + f, + [parenthesized( + "(", + &format_args!( + group(&elt.format()), + soft_line_break_or_space(), + group(&joined) + ), + ")" + )] + ) + } } } diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@conditional_expression.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@conditional_expression.py.snap index 57ef1898fb..2536898905 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@conditional_expression.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@conditional_expression.py.snap @@ -121,34 +121,20 @@ def something(): ): pass -@@ -59,26 +53,14 @@ - else "this one is a little shorter" +@@ -60,11 +54,9 @@ ) --generator_expression = ( + generator_expression = ( - ( - some_long_value_name_foo_bar_baz - if some_boolean_variable - else some_fallback_value_foo_bar_baz - ) -- for some_boolean_variable in some_iterable --) -+generator_expression = (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) - - - def limit_offset_sql(self, low_mark, high_mark): - """Return LIMIT/OFFSET SQL clause.""" - limit, offset = self._get_limit_offset_params(low_mark, high_mark) - return " ".join( -- sql -- for sql in ( -- "LIMIT %d" % limit if limit else None, -- ("OFFSET %d" % offset) if offset else None, -- ) -- if sql -+ (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) - ) - ++ some_long_value_name_foo_bar_baz ++ if some_boolean_variable ++ else some_fallback_value_foo_bar_baz + for some_boolean_variable in some_iterable + ) ``` @@ -210,14 +196,24 @@ nested = ( else "this one is a little shorter" ) -generator_expression = (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) +generator_expression = ( + some_long_value_name_foo_bar_baz + if some_boolean_variable + else some_fallback_value_foo_bar_baz + for some_boolean_variable in some_iterable +) def limit_offset_sql(self, low_mark, high_mark): """Return LIMIT/OFFSET SQL clause.""" limit, offset = self._get_limit_offset_params(low_mark, high_mark) return " ".join( - (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) + sql + for sql in ( + "LIMIT %d" % limit if limit else None, + ("OFFSET %d" % offset) if offset else None, + ) + if sql ) diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_generic.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_generic.py.snap index 6e8d106770..8e653596d3 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_generic.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_generic.py.snap @@ -134,7 +134,7 @@ with match() as match: def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]: -@@ -23,13 +23,11 @@ +@@ -23,11 +23,7 @@ pygram.python_grammar, ] @@ -145,14 +145,9 @@ with match() as match: - pass + NOT_YET_IMPLEMENTED_StmtMatch -- if all(version.is_python2() for version in target_versions): -+ if all( -+ (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) -+ ): + if all(version.is_python2() for version in target_versions): # Python 2-only code, so try Python 2 grammars. - return [ - # Python 2.7 with future print_function import -@@ -41,13 +39,11 @@ +@@ -41,13 +37,11 @@ re.match() match = a with match() as match: @@ -168,7 +163,7 @@ with match() as match: self.assertIs(x, False) self.assertEqual(y, 0) self.assertIs(z, x) -@@ -72,16 +68,12 @@ +@@ -72,16 +66,12 @@ def test_patma_155(self): x = 0 y = None @@ -187,7 +182,7 @@ with match() as match: # At least one of the above branches must have been taken, because every Python # version has exactly one of the two 'ASYNC_*' flags -@@ -99,9 +91,9 @@ +@@ -99,9 +89,9 @@ re.match() match = a with match() as match: @@ -231,9 +226,7 @@ def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]: NOT_YET_IMPLEMENTED_StmtMatch - if all( - (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) - ): + if all(version.is_python2() for version in target_versions): # Python 2-only code, so try Python 2 grammars. return [ # Python 2.7 with future print_function import diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pep_572_py310.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pep_572_py310.py.snap index 1204c05491..b8d306b641 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pep_572_py310.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pep_572_py310.py.snap @@ -27,7 +27,7 @@ f(x, (a := b + c for c in range(10)), y=z, **q) ```diff --- Black +++ Ruff -@@ -1,15 +1,20 @@ +@@ -1,7 +1,7 @@ # Unparenthesized walruses are now allowed in indices since Python 3.10. -x[a:=0] -x[a:=0, b:=1] @@ -37,25 +37,7 @@ f(x, (a := b + c for c in range(10)), y=z, **q) +x[5, b := 0] # Walruses are allowed inside generator expressions on function calls since 3.10. --if any(match := pattern_error.match(s) for s in buffer): -+if any((NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in [])): - if match.group(2) == data_not_available: - # Error OK to ignore. - pass - --f(a := b + c for c in range(10)) --f((a := b + c for c in range(10)), x) --f(y=(a := b + c for c in range(10))) --f(x, (a := b + c for c in range(10)), y=z, **q) -+f((NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in [])) -+f((NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []), x) -+f(y=(NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in [])) -+f( -+ x, -+ (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []), -+ y=z, -+ **q, -+) + if any(match := pattern_error.match(s) for s in buffer): ``` ## Ruff Output @@ -67,20 +49,15 @@ x[a := 0, b := 1] x[5, b := 0] # Walruses are allowed inside generator expressions on function calls since 3.10. -if any((NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in [])): +if any(match := pattern_error.match(s) for s in buffer): if match.group(2) == data_not_available: # Error OK to ignore. pass -f((NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in [])) -f((NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []), x) -f(y=(NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in [])) -f( - x, - (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []), - y=z, - **q, -) +f(a := b + c for c in range(10)) +f((a := b + c for c in range(10)), x) +f(y=(a := b + c for c in range(10))) +f(x, (a := b + c for c in range(10)), y=z, **q) ``` ## Black Output 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 deleted file mode 100644 index 4a12cae071..0000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_37__python37.py.snap +++ /dev/null @@ -1,142 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/py_37/python37.py ---- -## Input - -```py -#!/usr/bin/env python3.7 - - -def f(): - return (i * 2 async for i in arange(42)) - - -def g(): - return ( - something_long * something_long - async for something_long in async_generator(with_an_argument) - ) - - -async def func(): - if test: - out_batched = [ - i - async for i in aitertools._async_map( - self.async_inc, arange(8), batch_size=3 - ) - ] - - -def awaited_generator_value(n): - return (await awaitable for awaitable in awaitable_list) - - -def make_arange(n): - return (i * 2 for i in range(n) if await wrap(i)) -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -2,14 +2,11 @@ - - - def f(): -- return (i * 2 async for i in arange(42)) -+ return (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) - - - def g(): -- return ( -- something_long * something_long -- async for something_long in async_generator(with_an_argument) -- ) -+ return (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) - - - async def func(): -@@ -23,8 +20,8 @@ - - - def awaited_generator_value(n): -- return (await awaitable for awaitable in awaitable_list) -+ return (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) - - - def make_arange(n): -- return (i * 2 for i in range(n) if await wrap(i)) -+ return (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) -``` - -## Ruff Output - -```py -#!/usr/bin/env python3.7 - - -def f(): - return (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) - - -def g(): - return (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) - - -async def func(): - if test: - out_batched = [ - i - async for i in aitertools._async_map( - self.async_inc, arange(8), batch_size=3 - ) - ] - - -def awaited_generator_value(n): - return (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) - - -def make_arange(n): - return (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) -``` - -## Black Output - -```py -#!/usr/bin/env python3.7 - - -def f(): - return (i * 2 async for i in arange(42)) - - -def g(): - return ( - something_long * something_long - async for something_long in async_generator(with_an_argument) - ) - - -async def func(): - if test: - out_batched = [ - i - async for i in aitertools._async_map( - self.async_inc, arange(8), batch_size=3 - ) - ] - - -def awaited_generator_value(n): - return (await awaitable for awaitable in awaitable_list) - - -def make_arange(n): - return (i * 2 for i in range(n) if await wrap(i)) -``` - - 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 c411a16a6e..d9a42dae9b 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 @@ -83,12 +83,9 @@ while x := f(x): x = (y := 0) (z := (y := (x := 0))) (info := (name, phone, *rest)) -@@ -31,9 +31,9 @@ - len(lines := f.readlines()) - foo(x := 3, cat="vector") +@@ -33,7 +33,7 @@ foo(cat=(category := "vector")) --if any(len(longline := l) >= 100 for l in lines): -+if any((NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in [])): + if any(len(longline := l) >= 100 for l in lines): print(longline) -if env_base := os.environ.get("PYTHONUSERBASE", None): +if (env_base := os.environ.get("PYTHONUSERBASE", None)): @@ -143,7 +140,7 @@ x = (y := 0) len(lines := f.readlines()) foo(x := 3, cat="vector") foo(cat=(category := "vector")) -if any((NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in [])): +if any(len(longline := l) >= 100 for l in lines): print(longline) if (env_base := os.environ.get("PYTHONUSERBASE", None)): return env_base 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 b56a8edfee..12608cc9b6 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 @@ -363,21 +363,6 @@ 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 +187,10 @@ - (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) + j) for i in (1, 2, 3) for j in (1, 2, 3)) -+(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 []) - (*starred,) - { - "id": "1", @@ -208,24 +214,14 @@ what_is_up_with_those_new_coord_names = (coord_names | set(vars_to_create)) - set( vars_to_remove @@ -435,15 +420,6 @@ last_call() assert not Test, "Short message" assert this is ComplexTest and not requirements.fit_in_a_single_line( force=False -@@ -259,7 +255,7 @@ - ... - for y in (): - ... --for z in (i for i in (1, 2, 3)): -+for z in (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []): - ... - for i in call(): - ... @@ -328,13 +324,18 @@ ): return True @@ -670,10 +646,10 @@ numpy[np.newaxis, :] (SomeName) SomeName (Good, Bad, Ugly) -(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)) (*starred,) { "id": "1", @@ -738,7 +714,7 @@ for (x,) in (1,), (2,), (3,): ... for y in (): ... -for z in (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []): +for z in (i for i in (1, 2, 3)): ... for i in call(): ... 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 08f07ad1d3..ab61f02fb6 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,19 +4,21 @@ +@@ -4,19 +4,19 @@ slice[d::d] slice[0] slice[-1] @@ -63,16 +63,13 @@ ham[lower + offset : upper + offset] +slice[lambda NOT_YET_IMPLEMENTED_lambda: True :, None::] slice[1 or 2 : True and False] 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[ -+ (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) : x -+] +slice[ :: [i for i in range(42)]] async def f(): -@@ -27,5 +29,5 @@ +@@ -27,5 +27,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] @@ -101,9 +98,7 @@ slice[lambda NOT_YET_IMPLEMENTED_lambda: True : lambda NOT_YET_IMPLEMENTED_lambd slice[lambda NOT_YET_IMPLEMENTED_lambda: True :, None::] slice[1 or 2 : True and False] slice[not so_simple : 1 < val <= 10] -slice[ - (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) : x -] +slice[(1 for i in range(42)) : x] slice[ :: [i for i in range(42)]] 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 657ed0a424..1dd043b7e7 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 @@ -283,7 +283,7 @@ aaaaaaaaaaaaaa + [ ] ( aaaaaaaaaaaaaa - + (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) + + (a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) ) aaaaaaaaaaaaaa + { a diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__generator_exp.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__generator_exp.py.snap new file mode 100644 index 0000000000..2bc38c773c --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__generator_exp.py.snap @@ -0,0 +1,68 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/generator_exp.py +--- +## Input +```py +(a for b in c) + +# parens around generator expression not required +len(a for b in c) + +# parens around generator expression required +sum((a for b in c), start=0) + +# black keeps these atm, but intends to remove them in the future: +# https://github.com/psf/black/issues/2943 +f((1 for _ in a)) + +# make sure source parenthesis detection isn't fooled by these +f((1) for _ in (a)) + +# combination of the two above +f(((1) for _ in (a))) + + +# black keeps these atm, but intends to remove them in the future: +# https://github.com/psf/black/issues/2943 +len( + ( # leading + a for b in c + # trailing + ) +) +``` + +## Output +```py +(a for b in c) + +# parens around generator expression not required +len(a for b in c) + +# parens around generator expression required +sum((a for b in c), start=0) + +# black keeps these atm, but intends to remove them in the future: +# https://github.com/psf/black/issues/2943 +f(1 for _ in a) + +# make sure source parenthesis detection isn't fooled by these +f((1) for _ in (a)) + +# combination of the two above +f((1) for _ in (a)) + + +# black keeps these atm, but intends to remove them in the future: +# https://github.com/psf/black/issues/2943 +len( + # leading + a + for b in c + # trailing +) +``` + + + diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap index 38b70eb2ad..6cd292b2cc 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap @@ -163,7 +163,7 @@ with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb # currently unparsable by black: https://github.com/psf/black/issues/3678 -with (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []): +with (name_2 for name_0 in name_4): pass with (a, *b): pass