mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:25:17 +00:00
Avoid recommending __slots__
for classes that inherit from more than namedtuple
(#12531)
## Summary Closes https://github.com/astral-sh/ruff/issues/11887.
This commit is contained in:
parent
998bfe0847
commit
1fe4a5faed
3 changed files with 50 additions and 21 deletions
|
@ -1,4 +1,5 @@
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
from enum import Enum
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,3 +21,15 @@ class Good(namedtuple("foo", ["str", "int"])): # OK
|
||||||
|
|
||||||
class Good(NamedTuple): # Ok
|
class Good(NamedTuple): # Ok
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Good(namedtuple("foo", ["str", "int"]), Enum):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UnusualButStillBad(namedtuple("foo", ["str", "int"]), NamedTuple("foo", [("x", int, "y", int)])):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UnusualButStillBad(namedtuple("foo", ["str", "int"]), object):
|
||||||
|
pass
|
||||||
|
|
|
@ -92,23 +92,25 @@ pub(crate) fn no_slots_in_namedtuple_subclass(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the class has a call-based namedtuple in its bases,
|
/// If the class's bases consist solely of named tuples, return the kind of named tuple
|
||||||
/// return the kind of namedtuple it is
|
/// (either `collections.namedtuple()`, or `typing.NamedTuple()`). Otherwise, return `None`.
|
||||||
/// (either `collections.namedtuple()`, or `typing.NamedTuple()`).
|
|
||||||
/// Else, return `None`.
|
|
||||||
fn namedtuple_base(bases: &[Expr], semantic: &SemanticModel) -> Option<NamedTupleKind> {
|
fn namedtuple_base(bases: &[Expr], semantic: &SemanticModel) -> Option<NamedTupleKind> {
|
||||||
|
let mut kind = None;
|
||||||
for base in bases {
|
for base in bases {
|
||||||
let Expr::Call(ast::ExprCall { func, .. }) = base else {
|
if let Expr::Call(ast::ExprCall { func, .. }) = base {
|
||||||
continue;
|
// Ex) `collections.namedtuple()`
|
||||||
};
|
let qualified_name = semantic.resolve_qualified_name(func)?;
|
||||||
let Some(qualified_name) = semantic.resolve_qualified_name(func) else {
|
match qualified_name.segments() {
|
||||||
continue;
|
["collections", "namedtuple"] => kind = kind.or(Some(NamedTupleKind::Collections)),
|
||||||
};
|
["typing", "NamedTuple"] => kind = kind.or(Some(NamedTupleKind::Typing)),
|
||||||
match qualified_name.segments() {
|
// Ex) `enum.Enum`
|
||||||
["collections", "namedtuple"] => return Some(NamedTupleKind::Collections),
|
_ => return None,
|
||||||
["typing", "NamedTuple"] => return Some(NamedTupleKind::Typing),
|
}
|
||||||
_ => continue,
|
} else if !semantic.match_builtin_expr(base, "object") {
|
||||||
|
// Allow inheriting from `object`.
|
||||||
|
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
kind
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,30 @@
|
||||||
---
|
---
|
||||||
source: crates/ruff_linter/src/rules/flake8_slots/mod.rs
|
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
|
| ^^^ 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
|
| ^^^^^^^^^^^^^^^^^^ 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
|
||||||
|
|
|
||||||
|
|
||||||
|
SLOT002.py:34:7: SLOT002 Subclasses of `collections.namedtuple()` should define `__slots__`
|
||||||
|
|
|
||||||
|
34 | class UnusualButStillBad(namedtuple("foo", ["str", "int"]), object):
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ SLOT002
|
||||||
|
35 | pass
|
||||||
|
|
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue