Format lambda expression (#5806)

This commit is contained in:
Chris Pryer 2023-07-19 07:47:56 -04:00 committed by GitHub
parent 5d68ad9008
commit 38678142ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 327 additions and 155 deletions

View file

@ -0,0 +1,63 @@
# Leading
lambda x: x # Trailing
# Trailing
# Leading
lambda x, y: x # Trailing
# Trailing
# Leading
lambda x, y: x, y # Trailing
# Trailing
# Leading
lambda x, /, y: x # Trailing
# Trailing
# Leading
lambda x: lambda y: lambda z: x # Trailing
# Trailing
# Leading
lambda x: lambda y: lambda z: (x, y, z) # Trailing
# Trailing
# Leading
lambda x: lambda y: lambda z: (
x,
y,
z) # Trailing
# Trailing
# Leading
lambda x: lambda y: lambda z: (
x,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
z) # Trailing
# Trailing
a = (
lambda # Dangling
: 1
)

View file

@ -1,22 +1,62 @@
use crate::comments::dangling_comments;
use crate::context::PyFormatContext;
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter};
use crate::other::arguments::ArgumentsParentheses;
use crate::AsFormat;
use crate::{FormatNodeRule, PyFormatter};
use ruff_formatter::prelude::{space, text};
use ruff_formatter::{write, Buffer, FormatResult};
use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::node::{AnyNodeRef, AstNode};
use rustpython_parser::ast::ExprLambda;
#[derive(Default)]
pub struct FormatExprLambda;
impl FormatNodeRule<ExprLambda> for FormatExprLambda {
fn fmt_fields(&self, _item: &ExprLambda, f: &mut PyFormatter) -> FormatResult<()> {
fn fmt_fields(&self, item: &ExprLambda, f: &mut PyFormatter) -> FormatResult<()> {
let ExprLambda {
range: _,
args,
body,
} = item;
// It's possible for some `Arguments` of `lambda`s to be assigned dangling comments.
//
// a = (
// lambda # Dangling
// : 1
// )
let comments = f.context().comments().clone();
let dangling = comments.dangling_comments(args.as_any_node_ref());
write!(f, [text("lambda")])?;
if !args.args.is_empty() {
write!(
f,
[
space(),
args.format()
.with_options(ArgumentsParentheses::SkipInsideLambda),
]
)?;
}
write!(
f,
[not_yet_implemented_custom_text(
"lambda NOT_YET_IMPLEMENTED_lambda: True"
)]
[
text(":"),
space(),
body.format(),
dangling_comments(dangling)
]
)
}
fn fmt_dangling_comments(&self, _node: &ExprLambda, _f: &mut PyFormatter) -> FormatResult<()> {
// Override. Dangling comments are handled in `fmt_fields`.
Ok(())
}
}
impl NeedsParentheses for ExprLambda {

View file

@ -3,7 +3,7 @@ use std::usize;
use ruff_text_size::{TextRange, TextSize};
use rustpython_parser::ast::{Arguments, Ranged};
use ruff_formatter::{format_args, write};
use ruff_formatter::{format_args, write, FormatRuleWithOptions};
use ruff_python_ast::node::{AnyNodeRef, AstNode};
use ruff_python_whitespace::{first_non_trivia_token, SimpleTokenizer, Token, TokenKind};
@ -16,8 +16,28 @@ use crate::expression::parentheses::parenthesized;
use crate::prelude::*;
use crate::FormatNodeRule;
#[derive(Eq, PartialEq, Debug, Default)]
pub enum ArgumentsParentheses {
#[default]
Default,
/// Arguments should never be inside parentheses for lambda expressions.
SkipInsideLambda,
}
#[derive(Default)]
pub struct FormatArguments;
pub struct FormatArguments {
parentheses: ArgumentsParentheses,
}
impl FormatRuleWithOptions<Arguments, PyFormatContext<'_>> for FormatArguments {
type Options = ArgumentsParentheses;
fn with_options(mut self, options: Self::Options) -> Self {
self.parentheses = options;
self
}
}
impl FormatNodeRule<Arguments> for FormatArguments {
fn fmt_fields(&self, item: &Arguments, f: &mut PyFormatter) -> FormatResult<()> {
@ -170,7 +190,9 @@ impl FormatNodeRule<Arguments> for FormatArguments {
+ kwonlyargs.len()
+ usize::from(kwarg.is_some());
if num_arguments == 0 {
if self.parentheses == ArgumentsParentheses::SkipInsideLambda {
group(&format_inner).fmt(f)?;
} else if num_arguments == 0 {
// No arguments, format any dangling comments between `()`
write!(
f,

View file

@ -56,21 +56,15 @@ lambda a, b=1, /, c=2, d=3, *, e=4, f=5: 1
```diff
--- Black
+++ Ruff
@@ -35,10 +35,10 @@
@@ -35,7 +35,7 @@
pass
-lambda a, /: a
+lambda NOT_YET_IMPLEMENTED_lambda: True
+lambda: a
-lambda a, b, /, c, d, *, e, f: a
+lambda NOT_YET_IMPLEMENTED_lambda: True
lambda a, b, /, c, d, *, e, f: a
-lambda a, b, /, c, d, *args, e, f, **kwargs: args
+lambda NOT_YET_IMPLEMENTED_lambda: True
-lambda a, b=1, /, c=2, d=3, *, e=4, f=5: 1
+lambda NOT_YET_IMPLEMENTED_lambda: True
```
## Ruff Output
@ -113,13 +107,13 @@ def long_one_with_long_parameter_names(
pass
lambda NOT_YET_IMPLEMENTED_lambda: True
lambda: a
lambda NOT_YET_IMPLEMENTED_lambda: True
lambda a, b, /, c, d, *, e, f: a
lambda NOT_YET_IMPLEMENTED_lambda: True
lambda a, b, /, c, d, *args, e, f, **kwargs: args
lambda NOT_YET_IMPLEMENTED_lambda: True
lambda a, b=1, /, c=2, d=3, *, e=4, f=5: 1
```
## Black Output

View file

@ -68,21 +68,6 @@ while x := f(x):
pass
[y := f(x), y**2, y**3]
filtered_data = [y for x in data if (y := f(x)) is None]
@@ -19,10 +19,10 @@
pass
-lambda: (x := 1)
-(x := lambda: 1)
-(x := lambda: (y := 1))
-lambda line: (m := re.match(pattern, line)) and m.group(1)
+lambda NOT_YET_IMPLEMENTED_lambda: True
+(x := lambda NOT_YET_IMPLEMENTED_lambda: True)
+(x := lambda NOT_YET_IMPLEMENTED_lambda: True)
+lambda NOT_YET_IMPLEMENTED_lambda: True
x = (y := 0)
(z := (y := (x := 0)))
(info := (name, phone, *rest))
@@ -33,7 +33,7 @@
foo(cat=(category := "vector"))
if any(len(longline := l) >= 100 for l in lines):
@ -128,10 +113,10 @@ def foo(answer: (p := 42) = 5):
pass
lambda NOT_YET_IMPLEMENTED_lambda: True
(x := lambda NOT_YET_IMPLEMENTED_lambda: True)
(x := lambda NOT_YET_IMPLEMENTED_lambda: True)
lambda NOT_YET_IMPLEMENTED_lambda: True
lambda: (x := 1)
(x := lambda: 1)
(x := lambda: (y := 1))
lambda line: (m := re.match(pattern, line)) and m.group(1)
x = (y := 0)
(z := (y := (x := 0)))
(info := (name, phone, *rest))

View file

@ -1,48 +0,0 @@
---
source: crates/ruff_python_formatter/tests/fixtures.rs
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:
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
+pem_spam = lambda NOT_YET_IMPLEMENTED_lambda: True
+lambda NOT_YET_IMPLEMENTED_lambda: True
```
## Ruff Output
```py
for ((x in {}) or {})["a"] in x:
pass
pem_spam = lambda NOT_YET_IMPLEMENTED_lambda: True
lambda NOT_YET_IMPLEMENTED_lambda: True
```
## 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
```

View file

@ -274,34 +274,32 @@ last_call()
Name
None
True
@@ -31,18 +31,15 @@
@@ -31,7 +31,7 @@
-1
~int and not v1 ^ 123 + v2 | True
(~int) and (not ((v1 ^ (123 + v2)) | True))
-+(really ** -(confusing ** ~(operator**-precedence)))
++really ** -confusing ** ~operator**-precedence
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
-manylambdas = lambda x=lambda y=lambda z=1: z: y(): x()
lambda arg: None
lambda a=True: a
@@ -39,10 +39,11 @@
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: {
- "port1": port1_resource,
- "port2": port2_resource,
-}[port_id]
+lambda NOT_YET_IMPLEMENTED_lambda: True
+lambda NOT_YET_IMPLEMENTED_lambda: True
+lambda NOT_YET_IMPLEMENTED_lambda: True
+lambda NOT_YET_IMPLEMENTED_lambda: True
+lambda NOT_YET_IMPLEMENTED_lambda: True
+manylambdas = lambda NOT_YET_IMPLEMENTED_lambda: True
+foo = lambda NOT_YET_IMPLEMENTED_lambda: True
+foo = (
+ lambda port_id, ignore_missing: {"port1": port1_resource, "port2": port2_resource}[
+ port_id
+ ]
+)
1 if True else 2
str or None if True else str or bytes or None
(str or None) if True else (str or bytes or None)
@@ -57,7 +54,13 @@
@@ -57,7 +58,13 @@
{"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")}
@ -316,7 +314,7 @@ last_call()
()
(1,)
(1, 2)
@@ -101,7 +104,10 @@
@@ -101,7 +108,10 @@
{a: b * -2 for a, b in dictionary.items()}
{
k: v
@ -328,7 +326,7 @@ last_call()
}
Python3 > Python2 > COBOL
Life is Life
@@ -115,7 +121,7 @@
@@ -115,7 +125,7 @@
arg,
another,
kwarg="hey",
@ -337,7 +335,7 @@ last_call()
) # note: no trailing comma pre-3.6
call(*gidgets[:2])
call(a, *gidgets[:2])
@@ -152,13 +158,13 @@
@@ -152,13 +162,13 @@
slice[0:1]
slice[0:1:2]
slice[:]
@ -354,7 +352,7 @@ last_call()
numpy[0, :]
numpy[:, i]
numpy[0, :2]
@@ -172,7 +178,7 @@
@@ -172,7 +182,7 @@
numpy[1 : c + 1, c]
numpy[-(c + 1) :, d]
numpy[:, l[-2]]
@ -363,7 +361,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}
@@ -208,24 +214,14 @@
@@ -208,24 +218,14 @@
what_is_up_with_those_new_coord_names = (coord_names | set(vars_to_create)) - set(
vars_to_remove
)
@ -396,7 +394,7 @@ last_call()
Ø = set()
authors.łukasz.say_thanks()
mapping = {
@@ -237,10 +233,10 @@
@@ -237,10 +237,10 @@
def gen():
@ -411,16 +409,7 @@ last_call()
async def f():
@@ -249,7 +245,7 @@
print(*[] or [1])
print(**{1: 3} if False else {x: x for x in range(3)})
-print(*lambda x: x)
+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
@@ -328,13 +324,18 @@
@@ -328,13 +328,18 @@
):
return True
if (
@ -442,7 +431,7 @@ last_call()
^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l**aaaaaaaa.m // aaaaaaaa.n
):
return True
@@ -342,7 +343,8 @@
@@ -342,7 +347,8 @@
~aaaaaaaaaaaaaaaa.a
+ aaaaaaaaaaaaaaaa.b
- aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e
@ -492,13 +481,17 @@ not great
(~int) and (not ((v1 ^ (123 + v2)) | True))
+really ** -confusing ** ~operator**-precedence
flags & ~select.EPOLLIN and waiters.write_task is not None
lambda NOT_YET_IMPLEMENTED_lambda: True
lambda NOT_YET_IMPLEMENTED_lambda: True
lambda NOT_YET_IMPLEMENTED_lambda: True
lambda NOT_YET_IMPLEMENTED_lambda: True
lambda NOT_YET_IMPLEMENTED_lambda: True
manylambdas = lambda NOT_YET_IMPLEMENTED_lambda: True
foo = lambda NOT_YET_IMPLEMENTED_lambda: True
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
manylambdas = lambda x=lambda y=lambda z=1: z: y(): x()
foo = (
lambda port_id, ignore_missing: {"port1": port1_resource, "port2": port2_resource}[
port_id
]
)
1 if True else 2
str or None if True else str or bytes or None
(str or None) if True else (str or bytes or None)
@ -704,7 +697,7 @@ async def f():
print(*[] or [1])
print(**{1: 3} if False else {x: x for x in range(3)})
print(*lambda NOT_YET_IMPLEMENTED_lambda: True)
print(*lambda x: x)
assert not Test, "Short message"
assert this is ComplexTest and not requirements.fit_in_a_single_line(
force=False

View file

@ -269,9 +269,8 @@ d={'a':1,
+
# fmt: on
def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""):
- offset = attr.ib(default=attr.Factory(lambda: _r.uniform(1, 2)))
offset = attr.ib(default=attr.Factory(lambda: _r.uniform(1, 2)))
- assert task._cancel_stack[: len(old_stack)] == old_stack
+ offset = attr.ib(default=attr.Factory(lambda NOT_YET_IMPLEMENTED_lambda: True))
+ assert task._cancel_stack[ : len(old_stack)] == old_stack
@ -451,7 +450,7 @@ def function_signature_stress_test(
# fmt: on
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 NOT_YET_IMPLEMENTED_lambda: True))
offset = attr.ib(default=attr.Factory(lambda: _r.uniform(1, 2)))
assert task._cancel_stack[ : len(old_stack)] == old_stack

View file

@ -126,9 +126,8 @@ def __await__(): return (yield)
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)))
offset = attr.ib(default=attr.Factory(lambda: _r.uniform(10000, 200000)))
- assert task._cancel_stack[: len(old_stack)] == old_stack
+ offset = attr.ib(default=attr.Factory(lambda NOT_YET_IMPLEMENTED_lambda: True))
+ assert task._cancel_stack[ : len(old_stack)] == old_stack
@ -225,7 +224,7 @@ def function_signature_stress_test(
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 NOT_YET_IMPLEMENTED_lambda: True))
offset = attr.ib(default=attr.Factory(lambda: _r.uniform(10000, 200000)))
assert task._cancel_stack[ : len(old_stack)] == old_stack

View file

@ -80,7 +80,7 @@ return np.divide(
c = -(5**2)
d = 5 ** f["hi"]
-e = lazy(lambda **kwargs: 5)
+e = lazy(lambda NOT_YET_IMPLEMENTED_lambda: True)
+e = lazy(lambda: 5)
f = f() ** 5
g = a.b**c.d
h = 5 ** funcs.f()
@ -89,7 +89,7 @@ return np.divide(
c = -(5.0**2.0)
d = 5.0 ** f["hi"]
-e = lazy(lambda **kwargs: 5)
+e = lazy(lambda NOT_YET_IMPLEMENTED_lambda: True)
+e = lazy(lambda: 5)
f = f() ** 5.0
g = a.b**c.d
h = 5.0 ** funcs.f()
@ -115,7 +115,7 @@ a = 5**~4
b = 5 ** f()
c = -(5**2)
d = 5 ** f["hi"]
e = lazy(lambda NOT_YET_IMPLEMENTED_lambda: True)
e = lazy(lambda: 5)
f = f() ** 5
g = a.b**c.d
h = 5 ** funcs.f()
@ -134,7 +134,7 @@ a = 5.0**~4.0
b = 5.0 ** f()
c = -(5.0**2.0)
d = 5.0 ** f["hi"]
e = lazy(lambda NOT_YET_IMPLEMENTED_lambda: True)
e = lazy(lambda: 5)
f = f() ** 5.0
g = a.b**c.d
h = 5.0 ** funcs.f()

View file

@ -31,15 +31,6 @@ for (((((k, v))))) in d.items():
```diff
--- Black
+++ Ruff
@@ -5,7 +5,7 @@
# Don't touch tuple brackets after `in`
for module in (core, _unicodefun):
if hasattr(module, "_verify_python3_env"):
- module._verify_python3_env = lambda: None
+ module._verify_python3_env = lambda NOT_YET_IMPLEMENTED_lambda: True
# Brackets remain for long for loop lines
for (
@@ -17,9 +17,7 @@
for (
k,
@ -63,7 +54,7 @@ for k, v in d.items():
# Don't touch tuple brackets after `in`
for module in (core, _unicodefun):
if hasattr(module, "_verify_python3_env"):
module._verify_python3_env = lambda NOT_YET_IMPLEMENTED_lambda: True
module._verify_python3_env = lambda: None
# Brackets remain for long for loop lines
for (

View file

@ -56,11 +56,9 @@ ham[lower + offset : upper + offset]
slice[ham[c::d] :: 1]
slice[ham[cheese**2 : -1] : 1 : 1, ham[1:2]]
-slice[:-1:]
-slice[lambda: None : lambda: None]
-slice[lambda x, y, *args, really=2, **kwargs: None :, None::]
+slice[ : -1 :]
+slice[lambda NOT_YET_IMPLEMENTED_lambda: True : lambda NOT_YET_IMPLEMENTED_lambda: True]
+slice[lambda NOT_YET_IMPLEMENTED_lambda: True :, None::]
slice[lambda: None : lambda: None]
slice[lambda x, y, *args, really=2, **kwargs: None :, None::]
slice[1 or 2 : True and False]
slice[not so_simple : 1 < val <= 10]
slice[(1 for i in range(42)) : x]
@ -94,8 +92,8 @@ slice[c, c + 1, d::]
slice[ham[c::d] :: 1]
slice[ham[cheese**2 : -1] : 1 : 1, ham[1:2]]
slice[ : -1 :]
slice[lambda NOT_YET_IMPLEMENTED_lambda: True : lambda NOT_YET_IMPLEMENTED_lambda: True]
slice[lambda NOT_YET_IMPLEMENTED_lambda: True :, None::]
slice[lambda: None : lambda: None]
slice[lambda x, y, *args, really=2, **kwargs: None :, None::]
slice[1 or 2 : True and False]
slice[not so_simple : 1 < val <= 10]
slice[(1 for i in range(42)) : x]

View file

@ -0,0 +1,136 @@
---
source: crates/ruff_python_formatter/tests/fixtures.rs
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/lambda.py
---
## Input
```py
# Leading
lambda x: x # Trailing
# Trailing
# Leading
lambda x, y: x # Trailing
# Trailing
# Leading
lambda x, y: x, y # Trailing
# Trailing
# Leading
lambda x, /, y: x # Trailing
# Trailing
# Leading
lambda x: lambda y: lambda z: x # Trailing
# Trailing
# Leading
lambda x: lambda y: lambda z: (x, y, z) # Trailing
# Trailing
# Leading
lambda x: lambda y: lambda z: (
x,
y,
z) # Trailing
# Trailing
# Leading
lambda x: lambda y: lambda z: (
x,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
z) # Trailing
# Trailing
a = (
lambda # Dangling
: 1
)```
## Output
```py
# Leading
lambda x: x # Trailing
# Trailing
# Leading
lambda x, y: x # Trailing
# Trailing
# Leading
lambda x, y: x, y # Trailing
# Trailing
# Leading
lambda x, /, y: x # Trailing
# Trailing
# Leading
lambda x: lambda y: lambda z: x # Trailing
# Trailing
# Leading
lambda x: lambda y: lambda z: (x, y, z) # Trailing
# Trailing
# Leading
lambda x: lambda y: lambda z: (x, y, z) # Trailing
# Trailing
# Leading
lambda x: lambda y: lambda z: (
x,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
z,
) # Trailing
# Trailing
a = (
lambda: 1 # Dangling
)
```