[pylint] Ignore __slots__ with dynamic values (#11488)

## Summary

Closes https://github.com/astral-sh/ruff/issues/11333.
This commit is contained in:
Charlie Marsh 2024-05-22 00:18:01 -04:00 committed by GitHub
parent 3476e2f359
commit aa906b9c75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 41 additions and 6 deletions

View file

@ -82,3 +82,16 @@ class Foo:
@qux.setter @qux.setter
def qux(self, value): def qux(self, value):
self.bar = value / 2 self.bar = value / 2
class StudentG:
names = ("surname",)
__slots__ = (*names, "a")
def __init__(self, name, surname):
self.name = name
self.surname = surname # [assigning-non-slot]
self.setup()
def setup(self):
pass

View file

@ -98,6 +98,8 @@ impl Ranged for AttributeAssignment<'_> {
} }
/// Return a list of attributes that are assigned to but not included in `__slots__`. /// Return a list of attributes that are assigned to but not included in `__slots__`.
///
/// If the `__slots__` attribute cannot be statically determined, returns an empty vector.
fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> { fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
// First, collect all the attributes that are assigned to `__slots__`. // First, collect all the attributes that are assigned to `__slots__`.
let mut slots = FxHashSet::default(); let mut slots = FxHashSet::default();
@ -110,7 +112,13 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
}; };
if id == "__slots__" { if id == "__slots__" {
slots.extend(slots_attributes(value)); for attribute in slots_attributes(value) {
if let Some(attribute) = attribute {
slots.insert(attribute);
} else {
return vec![];
}
}
} }
} }
@ -125,7 +133,13 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
}; };
if id == "__slots__" { if id == "__slots__" {
slots.extend(slots_attributes(value)); for attribute in slots_attributes(value) {
if let Some(attribute) = attribute {
slots.insert(attribute);
} else {
return vec![];
}
}
} }
} }
@ -136,7 +150,13 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
}; };
if id == "__slots__" { if id == "__slots__" {
slots.extend(slots_attributes(value)); for attribute in slots_attributes(value) {
if let Some(attribute) = attribute {
slots.insert(attribute);
} else {
return vec![];
}
}
} }
} }
_ => {} _ => {}
@ -237,12 +257,14 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
} }
/// Return an iterator over the attributes enumerated in the given `__slots__` value. /// Return an iterator over the attributes enumerated in the given `__slots__` value.
fn slots_attributes(expr: &Expr) -> impl Iterator<Item = &str> { ///
/// If an attribute can't be statically determined, it will be `None`.
fn slots_attributes(expr: &Expr) -> impl Iterator<Item = Option<&str>> {
// Ex) `__slots__ = ("name",)` // Ex) `__slots__ = ("name",)`
let elts_iter = match expr { let elts_iter = match expr {
Expr::Tuple(ast::ExprTuple { elts, .. }) Expr::Tuple(ast::ExprTuple { elts, .. })
| Expr::List(ast::ExprList { elts, .. }) | Expr::List(ast::ExprList { elts, .. })
| Expr::Set(ast::ExprSet { elts, .. }) => Some(elts.iter().filter_map(|elt| match elt { | Expr::Set(ast::ExprSet { elts, .. }) => Some(elts.iter().map(|elt| match elt {
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => Some(value.to_str()), Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => Some(value.to_str()),
_ => None, _ => None,
})), })),
@ -251,7 +273,7 @@ fn slots_attributes(expr: &Expr) -> impl Iterator<Item = &str> {
// Ex) `__slots__ = {"name": ...}` // Ex) `__slots__ = {"name": ...}`
let keys_iter = match expr { let keys_iter = match expr {
Expr::Dict(dict) => Some(dict.iter_keys().filter_map(|key| match key { Expr::Dict(dict) => Some(dict.iter_keys().map(|key| match key {
Some(Expr::StringLiteral(ast::ExprStringLiteral { value, .. })) => Some(value.to_str()), Some(Expr::StringLiteral(ast::ExprStringLiteral { value, .. })) => Some(value.to_str()),
_ => None, _ => None,
})), })),