Skip to content

Commit

Permalink
Avoid recommending __slots__ for classes that inherit from more than …
Browse files Browse the repository at this point in the history
…namedtuple
  • Loading branch information
charliermarsh committed Jul 26, 2024
1 parent 6f4db86 commit 496384e
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from collections import namedtuple
from enum import Enum
from typing import NamedTuple


Expand All @@ -20,3 +21,11 @@ class Good(namedtuple("foo", ["str", "int"])): # OK

class Good(NamedTuple): # Ok
pass


class Good(namedtuple("foo", ["str", "int"]), Enum):
pass


class UnusualButStillBad(namedtuple("foo", ["str", "int"]), NamedTuple("foo", [("x", int, "y", int)])):
pass
Original file line number Diff line number Diff line change
Expand Up @@ -92,23 +92,18 @@ pub(crate) fn no_slots_in_namedtuple_subclass(
}
}

/// If the class has a call-based namedtuple in its bases,
/// return the kind of namedtuple it is
/// (either `collections.namedtuple()`, or `typing.NamedTuple()`).
/// Else, return `None`.
/// If the class's bases consist solely of named tuples, return the kind of named tuple
/// (either `collections.namedtuple()`, or `typing.NamedTuple()`). Otherwise, return `None`.
fn namedtuple_base(bases: &[Expr], semantic: &SemanticModel) -> Option<NamedTupleKind> {
let mut kind = None;
for base in bases {
let Expr::Call(ast::ExprCall { func, .. }) = base else {
continue;
};
let Some(qualified_name) = semantic.resolve_qualified_name(func) else {
continue;
};
let ast::ExprCall { func, .. } = base.as_call_expr()?;
let qualified_name = semantic.resolve_qualified_name(func)?;
match qualified_name.segments() {
["collections", "namedtuple"] => return Some(NamedTupleKind::Collections),
["typing", "NamedTuple"] => return Some(NamedTupleKind::Typing),
_ => continue,
["collections", "namedtuple"] => kind = kind.or(Some(NamedTupleKind::Collections)),
["typing", "NamedTuple"] => kind = kind.or(Some(NamedTupleKind::Typing)),
_ => return None,
}
}
None
kind
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
---
source: crates/ruff_linter/src/rules/flake8_slots/mod.rs
---
SLOT002.py:5:7: SLOT002 Subclasses of `collections.namedtuple()` should define `__slots__`
SLOT002.py:6:7: SLOT002 Subclasses of `collections.namedtuple()` should define `__slots__`
|
5 | class Bad(namedtuple("foo", ["str", "int"])): # SLOT002
6 | class Bad(namedtuple("foo", ["str", "int"])): # SLOT002
| ^^^ SLOT002
6 | pass
7 | pass
|

SLOT002.py:9:7: SLOT002 Subclasses of call-based `typing.NamedTuple()` should define `__slots__`
SLOT002.py:10:7: SLOT002 Subclasses of call-based `typing.NamedTuple()` should define `__slots__`
|
9 | class UnusualButStillBad(NamedTuple("foo", [("x", int, "y", int)])): # SLOT002
10 | class UnusualButStillBad(NamedTuple("foo", [("x", int, "y", int)])): # SLOT002
| ^^^^^^^^^^^^^^^^^^ SLOT002
10 | pass
11 | pass
|

SLOT002.py:30:7: SLOT002 Subclasses of `collections.namedtuple()` should define `__slots__`
|
30 | class UnusualButStillBad(namedtuple("foo", ["str", "int"]), NamedTuple("foo", [("x", int, "y", int)])):
| ^^^^^^^^^^^^^^^^^^ SLOT002
31 | pass
|

0 comments on commit 496384e

Please sign in to comment.