diff --git a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI015.py b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI015.py index 25e8da5413..37a4f4d867 100644 --- a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI015.py +++ b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI015.py @@ -84,3 +84,10 @@ class Class1: # We shouldn't emit Y015 for __all__ __all__ = ["Class1"] + +# Ignore the following for PYI015 +field26 = typing.Sequence[int] +field27 = list[str] +field28 = builtins.str +field29 = str +field30 = str | bytes | None diff --git a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI015.pyi b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI015.pyi index 3a2c0ba594..860ee255fb 100644 --- a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI015.pyi +++ b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI015.pyi @@ -91,3 +91,10 @@ class Class1: # We shouldn't emit Y015 for __all__ __all__ = ["Class1"] + +# Ignore the following for PYI015 +field26 = typing.Sequence[int] +field27 = list[str] +field28 = builtins.str +field29 = str +field30 = str | bytes | None diff --git a/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs b/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs index c99c6f6d55..08c1fb261c 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs @@ -219,6 +219,40 @@ fn is_valid_default_value_with_annotation( false } +/// Returns `true` if an [`Expr`] appears to be a valid PEP 604 union. (e.g. `int | None`) +fn is_valid_pep_604_union(annotation: &Expr) -> bool { + match &annotation.node { + ExprKind::BinOp { + left, + op: Operator::BitOr, + right, + } => is_valid_pep_604_union(left) && is_valid_pep_604_union(right), + ExprKind::Name { .. } + | ExprKind::Subscript { .. } + | ExprKind::Attribute { .. } + | ExprKind::Constant { + value: Constant::None, + .. + } => true, + _ => false, + } +} + +/// Returns `true` if an [`Expr`] appears to be a valid default value without an annotation. +fn is_valid_default_value_without_annotation(default: &Expr) -> bool { + matches!( + &default.node, + ExprKind::Call { .. } + | ExprKind::Name { .. } + | ExprKind::Attribute { .. } + | ExprKind::Subscript { .. } + | ExprKind::Constant { + value: Constant::Ellipsis | Constant::None, + .. + } + ) || is_valid_pep_604_union(default) +} + /// Returns `true` if an [`Expr`] appears to be `TypeVar`, `TypeVarTuple`, `NewType`, or `ParamSpec` /// call. fn is_type_var_like_call(context: &Context, expr: &Expr) -> bool { @@ -372,6 +406,9 @@ pub fn assignment_default_in_stub(checker: &mut Checker, targets: &[Expr], value if is_type_var_like_call(&checker.ctx, value) { return; } + if is_valid_default_value_without_annotation(value) { + return; + } if is_valid_default_value_with_annotation(value, checker, true) { return; }