mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-16 00:20:22 +00:00
Preserve generator parentheses in single argument call expressions (#7226)
This commit is contained in:
parent
e376c3ff7e
commit
a352f2f092
5 changed files with 67 additions and 14 deletions
|
@ -16,6 +16,11 @@ f((1) for _ in (a))
|
||||||
# combination of the two above
|
# combination of the two above
|
||||||
f(((1) for _ in (a)))
|
f(((1) for _ in (a)))
|
||||||
|
|
||||||
|
bases = tuple(
|
||||||
|
(base._meta.label_lower if hasattr(base, "_meta") else base)
|
||||||
|
for base in flattened_bases
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# black keeps these atm, but intends to remove them in the future:
|
# black keeps these atm, but intends to remove them in the future:
|
||||||
# https://github.com/psf/black/issues/2943
|
# https://github.com/psf/black/issues/2943
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use ruff_formatter::{format_args, write, Buffer, FormatResult, FormatRuleWithOptions};
|
use ruff_formatter::{format_args, write, FormatRuleWithOptions};
|
||||||
use ruff_python_ast::node::AnyNodeRef;
|
use ruff_python_ast::node::AnyNodeRef;
|
||||||
use ruff_python_ast::ExprGeneratorExp;
|
use ruff_python_ast::ExprGeneratorExp;
|
||||||
|
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||||
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::comments::SourceComment;
|
use crate::comments::SourceComment;
|
||||||
|
|
||||||
use crate::expression::parentheses::{parenthesized, NeedsParentheses, OptionalParentheses};
|
use crate::expression::parentheses::{parenthesized, NeedsParentheses, OptionalParentheses};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
@ -12,12 +13,13 @@ pub enum GeneratorExpParentheses {
|
||||||
#[default]
|
#[default]
|
||||||
Default,
|
Default,
|
||||||
|
|
||||||
/// Skip parens if the generator is the only argument to a function and doesn't contain any
|
/// Skips the parentheses if they aren't present in the source code. Used when formatting call expressions
|
||||||
/// dangling comments. For example:
|
/// because the parentheses are optional if the generator is the **only** argument:
|
||||||
|
///
|
||||||
/// ```python
|
/// ```python
|
||||||
/// all(x for y in z)`
|
/// all(x for y in z)`
|
||||||
/// ```
|
/// ```
|
||||||
StripIfOnlyFunctionArg,
|
Preserve,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormatRuleWithOptions<ExprGeneratorExp, PyFormatContext<'_>> for FormatExprGeneratorExp {
|
impl FormatRuleWithOptions<ExprGeneratorExp, PyFormatContext<'_>> for FormatExprGeneratorExp {
|
||||||
|
@ -51,8 +53,9 @@ impl FormatNodeRule<ExprGeneratorExp> for FormatExprGeneratorExp {
|
||||||
let comments = f.context().comments().clone();
|
let comments = f.context().comments().clone();
|
||||||
let dangling = comments.dangling(item);
|
let dangling = comments.dangling(item);
|
||||||
|
|
||||||
if self.parentheses == GeneratorExpParentheses::StripIfOnlyFunctionArg
|
if self.parentheses == GeneratorExpParentheses::Preserve
|
||||||
&& dangling.is_empty()
|
&& dangling.is_empty()
|
||||||
|
&& !is_generator_parenthesized(item, f.context().source())
|
||||||
{
|
{
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
@ -94,3 +97,36 @@ impl NeedsParentheses for ExprGeneratorExp {
|
||||||
OptionalParentheses::Never
|
OptionalParentheses::Never
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_generator_parenthesized(generator: &ExprGeneratorExp, source: &str) -> bool {
|
||||||
|
// / Count the number of open parentheses between the start of the tuple and the first element.
|
||||||
|
let open_parentheses_count = SimpleTokenizer::new(
|
||||||
|
source,
|
||||||
|
TextRange::new(generator.start(), generator.elt.start()),
|
||||||
|
)
|
||||||
|
.skip_trivia()
|
||||||
|
.filter(|token| token.kind() == SimpleTokenKind::LParen)
|
||||||
|
.count();
|
||||||
|
if open_parentheses_count == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count the number of parentheses between the end of the first element and its trailing comma.
|
||||||
|
let close_parentheses_count = SimpleTokenizer::new(
|
||||||
|
source,
|
||||||
|
TextRange::new(
|
||||||
|
generator.elt.end(),
|
||||||
|
generator
|
||||||
|
.generators
|
||||||
|
.first()
|
||||||
|
.map_or(generator.end(), Ranged::start),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.skip_trivia()
|
||||||
|
.filter(|token| token.kind() == SimpleTokenKind::RParen)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
// If the number of open parentheses is greater than the number of close parentheses, the generator
|
||||||
|
// is parenthesized.
|
||||||
|
open_parentheses_count > close_parentheses_count
|
||||||
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ impl FormatNodeRule<Arguments> for FormatArguments {
|
||||||
generator_exp,
|
generator_exp,
|
||||||
&generator_exp
|
&generator_exp
|
||||||
.format()
|
.format()
|
||||||
.with_options(GeneratorExpParentheses::StripIfOnlyFunctionArg),
|
.with_options(GeneratorExpParentheses::Preserve),
|
||||||
),
|
),
|
||||||
other => {
|
other => {
|
||||||
let parentheses =
|
let parentheses =
|
||||||
|
|
|
@ -386,10 +386,10 @@ threshold_date = datetime.datetime.now() - datetime.timedelta( # comment
|
||||||
)
|
)
|
||||||
|
|
||||||
# Parenthesized and opening-parenthesis comments
|
# Parenthesized and opening-parenthesis comments
|
||||||
func(x for x in y)
|
func((x for x in y))
|
||||||
|
|
||||||
func( # outer comment
|
func( # outer comment
|
||||||
x for x in y
|
(x for x in y)
|
||||||
)
|
)
|
||||||
|
|
||||||
func(
|
func(
|
||||||
|
@ -399,9 +399,11 @@ func(
|
||||||
)
|
)
|
||||||
|
|
||||||
func(
|
func(
|
||||||
# inner comment
|
(
|
||||||
x
|
# inner comment
|
||||||
for x in y
|
x
|
||||||
|
for x in y
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
func( # outer comment
|
func( # outer comment
|
||||||
|
|
|
@ -22,6 +22,11 @@ f((1) for _ in (a))
|
||||||
# combination of the two above
|
# combination of the two above
|
||||||
f(((1) for _ in (a)))
|
f(((1) for _ in (a)))
|
||||||
|
|
||||||
|
bases = tuple(
|
||||||
|
(base._meta.label_lower if hasattr(base, "_meta") else base)
|
||||||
|
for base in flattened_bases
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# black keeps these atm, but intends to remove them in the future:
|
# black keeps these atm, but intends to remove them in the future:
|
||||||
# https://github.com/psf/black/issues/2943
|
# https://github.com/psf/black/issues/2943
|
||||||
|
@ -67,13 +72,18 @@ sum((a for b in c), start=0)
|
||||||
|
|
||||||
# black keeps these atm, but intends to remove them in the future:
|
# black keeps these atm, but intends to remove them in the future:
|
||||||
# https://github.com/psf/black/issues/2943
|
# https://github.com/psf/black/issues/2943
|
||||||
f(1 for _ in a)
|
f((1 for _ in a))
|
||||||
|
|
||||||
# make sure source parenthesis detection isn't fooled by these
|
# make sure source parenthesis detection isn't fooled by these
|
||||||
f((1) for _ in (a))
|
f((1) for _ in (a))
|
||||||
|
|
||||||
# combination of the two above
|
# combination of the two above
|
||||||
f((1) for _ in (a))
|
f(((1) for _ in (a)))
|
||||||
|
|
||||||
|
bases = tuple(
|
||||||
|
(base._meta.label_lower if hasattr(base, "_meta") else base)
|
||||||
|
for base in flattened_bases
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# black keeps these atm, but intends to remove them in the future:
|
# black keeps these atm, but intends to remove them in the future:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue