Skip to content

Commit

Permalink
Ignore __slots__ with dynamic values
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed May 22, 2024
1 parent b0731ef commit 9cac696
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,16 @@ def qux(self):
@qux.setter
def qux(self, value):
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
34 changes: 28 additions & 6 deletions crates/ruff_linter/src/rules/pylint/rules/non_slot_assignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ impl Ranged for AttributeAssignment<'_> {
}

/// 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 list.
fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
// First, collect all the attributes that are assigned to `__slots__`.
let mut slots = FxHashSet::default();
Expand All @@ -110,7 +112,13 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
};

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![];
}
}
}
}

Expand All @@ -125,7 +133,13 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
};

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![];
}
}
}
}

Expand All @@ -136,7 +150,13 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
};

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![];
}
}
}
}
_ => {}
Expand Down Expand Up @@ -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.
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",)`
let elts_iter = match expr {
Expr::Tuple(ast::ExprTuple { 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()),
_ => None,
})),
Expand All @@ -251,7 +273,7 @@ fn slots_attributes(expr: &Expr) -> impl Iterator<Item = &str> {

// Ex) `__slots__ = {"name": ...}`
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()),
_ => None,
})),
Expand Down

0 comments on commit 9cac696

Please sign in to comment.