-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Format expr generator exp #5804
Format expr generator exp #5804
Conversation
PR Check ResultsEcosystem✅ ecosystem check detected no changes. BenchmarkLinux
Windows
|
#5829 fixes the |
match argument { | ||
Expr::GeneratorExp(generator_exp) => joiner.entry( | ||
generator_exp, | ||
&generator_exp | ||
.format() | ||
.with_options(GeneratorExpParentheses::StripIfOnlyFunctionArg), | ||
), | ||
other => joiner.entry(other, &other.format().with_options(parentheses)), | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, that Black has a custom logic especially for generators.
Can you add an example with a GeneratorExpr
that also has comments?
len(
( # leading
a for b in c
# trailing
)
)
Nit: Let's move the parentheses
check from line 53..59 into the other =>
branch. We don't need to test if the argument is parenthesized if we're going to remove the parentheses anyway
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this isn't based on black but rather the rules for generator expressions. see e.g. "details, §2b" in the pep https://peps.python.org/pep-0289/#the-details
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that your example will differ to black, in my understanding due to the same black bug that keeps _existing parens
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that your example will differ to black, in my understanding due to the same black bug that keeps _existing parens
Black's behavior is to always preserve parentheses in nested expressions and I think we should do the same for generators, except if we have very good reasons not to (for consistency).
I think the idiomatic way to implement this then is to change the generator's NeedsParentheses
implementation to return Never
if it is the only argument, and Always
otherwise (except there are other places where the parentheses should be omitted).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that your example will differ to black, in my understanding due to the same black bug that keeps _existing parens
Black's behavior is to always preserve parentheses in nested expressions and I think we should do the same for generators, except if we have very good reasons not to (for consistency).
is this a reference to https://github.com/astral-sh/ruff/pull/5804/files#diff-dba753754ca8da36599acf528a6bfe006404be2c4cd9d00f18db1a3cf1a7ec21R9
or https://github.com/astral-sh/ruff/pull/5804/files#diff-dba753754ca8da36599acf528a6bfe006404be2c4cd9d00f18db1a3cf1a7ec21R19
?
I think the idiomatic way to implement this then is to change the generator's
NeedsParentheses
implementation to returnNever
if it is the only argument, andAlways
otherwise (except there are other places where the parentheses should be omitted).
how does NeedsParentheses
figure out if it's a no-sibling function argument?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok. i'm afraid i don't quite understand your fix suggestion. atm NeedsParentheses
(afict) is only called if there is a maybe_parenthesize_expression
wrapper. is that right? if so, where are you suggesting that should go? apologies if i'm missing something obvious here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The proposal with NeedsParentheses
is mainly if we want to match Black's behavior:
- Because, by default, expressions preserve their parentheses. Meaning the generators would have needed parentheses already before. Or are you aware of situations where a generator is not parenthesized in the source, but would need to be parenthesized after formatting (e.g. because we insert a trailing comma)?
- Overriding
NeedsParentheses
and returningAlways
will add/preserve parentheses in the places where we, otherwise would omit them (e.g. in an if header).
The reason why I think that NeedsParentheses
would be a good fit is, because it should inform the formatted when it is necessary to parenthesize an expression. Tuples are different, because they have their own parentheses (you can have a parenthesized tuple expression ((a, b))
, but (a, b)
is a tuple expression with parentheses. Okay this sounds confusing. One is about it is a parenthesized expression, the other is that tuples have their own parentheses)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe i'm misunderstanding you, but in my understanding of generator expressions, they do have their "own parentheses" just like tuples do (but they can be omitted in some contexts. (fewer contexts than for tuples, only if they are a sole call argument))
maybe we merge this without preserving "double wrapped" generators and see how common when we compare ourselves against black on the various code bases we test. in general i'm in favour of matching black closer if that helps adoption
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I'm sorry. That's my bad. I assumed that parenthesizing the generator is optional. But from Python's full grammar:
genexp:
| '(' ( assignment_expression | expression !':=') for_if_clauses ')'
(Oddly, the parens are not optional)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i wonder if the function call case is covered here
primary:
| primary '.' NAME
| primary genexp <-- generator expr instead of brackets and arguments
| primary '(' [arguments] ')' <-- (regular function call)
49c5aec
to
bcd7929
Compare
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/generator_exp.py
Outdated
Show resolved
Hide resolved
bcd7929
to
2e13910
Compare
Summary
Format
GeneratorExp
note: I manually edited
generated.rs
sinceFormatExprGeneratorExp
now has a value and needs to be instantiated. it looks like a few others might also be in the same boat (they also break if i re-geneate)Similarity index for django: 0.978 -> 0.981
Fixes #5676
Test Plan
snapshots