-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
441 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
class Bad(str): # SLOT000 | ||
pass | ||
|
||
|
||
class Good(str): # Ok | ||
__slots__ = ["foo"] |
21 changes: 21 additions & 0 deletions
21
crates/ruff/resources/test/fixtures/flake8_slots/SLOT001.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
class Bad(tuple): # SLOT001 | ||
pass | ||
|
||
|
||
class Good(tuple): # Ok | ||
__slots__ = ("foo",) | ||
|
||
|
||
from typing import Tuple | ||
|
||
|
||
class Bad(Tuple): # SLOT001 | ||
pass | ||
|
||
|
||
class Bad(Tuple[str, int, float]): # SLOT001 | ||
pass | ||
|
||
|
||
class Good(Tuple[str, int, float]): # OK | ||
__slots__ = ("foo",) |
14 changes: 14 additions & 0 deletions
14
crates/ruff/resources/test/fixtures/flake8_slots/SLOT002.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
from collections import namedtuple | ||
from typing import NamedTuple | ||
|
||
|
||
class Bad(namedtuple("foo", ["str", "int"])): # SLOT002 | ||
pass | ||
|
||
|
||
class Good(namedtuple("foo", ["str", "int"])): # OK | ||
__slots__ = ("foo",) | ||
|
||
|
||
class Good(NamedTuple): # Ok | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
//! Rules from [flake8-slots](https://pypi.org/project/flake8-slots/). | ||
pub(crate) mod rules; | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use std::path::Path; | ||
|
||
use anyhow::Result; | ||
use test_case::test_case; | ||
|
||
use crate::registry::Rule; | ||
use crate::test::test_path; | ||
use crate::{assert_messages, settings}; | ||
|
||
#[test_case(Rule::NoSlotsInStrSubclass, Path::new("SLOT000.py"))] | ||
#[test_case(Rule::NoSlotsInTupleSubclass, Path::new("SLOT001.py"))] | ||
#[test_case(Rule::NoSlotsInNamedtupleSubclass, Path::new("SLOT002.py"))] | ||
fn rules(rule_code: Rule, path: &Path) -> Result<()> { | ||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); | ||
let diagnostics = test_path( | ||
Path::new("flake8_slots").join(path).as_path(), | ||
&settings::Settings::for_rule(rule_code), | ||
)?; | ||
assert_messages!(snapshot, diagnostics); | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
use rustpython_parser::ast::{self, Expr, Stmt}; | ||
|
||
/// Return `true` if the given body contains a `__slots__` assignment. | ||
pub(super) fn has_slots(body: &[Stmt]) -> bool { | ||
for stmt in body { | ||
match stmt { | ||
Stmt::Assign(ast::StmtAssign { targets, .. }) => { | ||
for target in targets { | ||
if let Expr::Name(ast::ExprName { id, .. }) = target { | ||
if id.as_str() == "__slots__" { | ||
return true; | ||
} | ||
} | ||
} | ||
} | ||
Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => { | ||
if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() { | ||
if id.as_str() == "__slots__" { | ||
return true; | ||
} | ||
} | ||
} | ||
_ => {} | ||
} | ||
} | ||
false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
pub(crate) use no_slots_in_namedtuple_subclass::{ | ||
no_slots_in_namedtuple_subclass, NoSlotsInNamedtupleSubclass, | ||
}; | ||
pub(crate) use no_slots_in_str_subclass::{no_slots_in_str_subclass, NoSlotsInStrSubclass}; | ||
pub(crate) use no_slots_in_tuple_subclass::{no_slots_in_tuple_subclass, NoSlotsInTupleSubclass}; | ||
|
||
mod helpers; | ||
mod no_slots_in_namedtuple_subclass; | ||
mod no_slots_in_str_subclass; | ||
mod no_slots_in_tuple_subclass; |
84 changes: 84 additions & 0 deletions
84
crates/ruff/src/rules/flake8_slots/rules/no_slots_in_namedtuple_subclass.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
use rustpython_parser::ast; | ||
use rustpython_parser::ast::{Expr, StmtClassDef}; | ||
|
||
use ruff_diagnostics::{Diagnostic, Violation}; | ||
use ruff_macros::{derive_message_formats, violation}; | ||
use ruff_python_ast::helpers::identifier_range; | ||
use ruff_python_ast::prelude::Stmt; | ||
|
||
use crate::checkers::ast::Checker; | ||
use crate::rules::flake8_slots::rules::helpers::has_slots; | ||
|
||
/// ## What it does | ||
/// Checks for subclasses of `collections.namedtuple` that lack a `__slots__` | ||
/// definition. | ||
/// | ||
/// ## Why is this bad? | ||
/// In Python, the `__slots__` attribute allows you to explicitly define the | ||
/// attributes (instance variables) that a class can have. By default, Python | ||
/// uses a dictionary to store an object's attributes, which incurs some memory | ||
/// overhead. However, when `__slots__` is defined, Python uses a more compact | ||
/// internal structure to store the object's attributes, resulting in memory | ||
/// savings. | ||
/// | ||
/// Subclasses of `namedtuple` inherit all the attributes and methods of the | ||
/// built-in `namedtuple` class. Since tuples are typically immutable, they | ||
/// don't require additional attributes beyond what the `namedtuple` class | ||
/// provides. Defining `__slots__` for subclasses of `namedtuple` prevents the | ||
/// creation of a dictionary for each instance, reducing memory consumption. | ||
/// | ||
/// ## Example | ||
/// ```python | ||
/// from collections import namedtuple | ||
/// | ||
/// | ||
/// class Foo(namedtuple("foo", ["str", "int"])): | ||
/// pass | ||
/// ``` | ||
/// | ||
/// Use instead: | ||
/// ```python | ||
/// from collections import namedtuple | ||
/// | ||
/// | ||
/// class Foo(namedtuple("foo", ["str", "int"])): | ||
/// __slots__ = () | ||
/// ``` | ||
/// | ||
/// ## References | ||
/// - [Python documentation: `__slots__`](https://docs.python.org/3.7/reference/datamodel.html#slots) | ||
#[violation] | ||
pub struct NoSlotsInNamedtupleSubclass; | ||
|
||
impl Violation for NoSlotsInNamedtupleSubclass { | ||
#[derive_message_formats] | ||
fn message(&self) -> String { | ||
format!("Subclasses of `collections.namedtuple()` should define `__slots__`") | ||
} | ||
} | ||
|
||
/// SLOT002 | ||
pub(crate) fn no_slots_in_namedtuple_subclass( | ||
checker: &mut Checker, | ||
stmt: &Stmt, | ||
class: &StmtClassDef, | ||
) { | ||
if class.bases.iter().any(|base| { | ||
let Expr::Call(ast::ExprCall { func, .. }) = base else { | ||
return false; | ||
}; | ||
checker | ||
.semantic_model() | ||
.resolve_call_path(func) | ||
.map_or(false, |call_path| { | ||
matches!(call_path.as_slice(), ["collections", "namedtuple"]) | ||
}) | ||
}) { | ||
if !has_slots(&class.body) { | ||
checker.diagnostics.push(Diagnostic::new( | ||
NoSlotsInNamedtupleSubclass, | ||
identifier_range(stmt, checker.locator), | ||
)); | ||
} | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
crates/ruff/src/rules/flake8_slots/rules/no_slots_in_str_subclass.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
use rustpython_parser::ast::{Stmt, StmtClassDef}; | ||
|
||
use ruff_diagnostics::{Diagnostic, Violation}; | ||
use ruff_macros::{derive_message_formats, violation}; | ||
use ruff_python_ast::helpers::identifier_range; | ||
|
||
use crate::checkers::ast::Checker; | ||
use crate::rules::flake8_slots::rules::helpers::has_slots; | ||
|
||
/// ## What it does | ||
/// Checks for subclasses of `str` that lack a `__slots__` definition. | ||
/// | ||
/// ## Why is this bad? | ||
/// In Python, the `__slots__` attribute allows you to explicitly define the | ||
/// attributes (instance variables) that a class can have. By default, Python | ||
/// uses a dictionary to store an object's attributes, which incurs some memory | ||
/// overhead. However, when `__slots__` is defined, Python uses a more compact | ||
/// internal structure to store the object's attributes, resulting in memory | ||
/// savings. | ||
/// | ||
/// Subclasses of `str` inherit all the attributes and methods of the built-in | ||
/// `str` class. Since strings are typically immutable, they don't require | ||
/// additional attributes beyond what the `str` class provides. Defining | ||
/// `__slots__` for subclasses of `str` prevents the creation of a dictionary | ||
/// for each instance, reducing memory consumption. | ||
/// | ||
/// ## Example | ||
/// ```python | ||
/// class Foo(str): | ||
/// pass | ||
/// ``` | ||
/// | ||
/// Use instead: | ||
/// ```python | ||
/// class Foo(str): | ||
/// __slots__ = () | ||
/// ``` | ||
/// | ||
/// ## References | ||
/// - [Python documentation: `__slots__`](https://docs.python.org/3.7/reference/datamodel.html#slots) | ||
#[violation] | ||
pub struct NoSlotsInStrSubclass; | ||
|
||
impl Violation for NoSlotsInStrSubclass { | ||
#[derive_message_formats] | ||
fn message(&self) -> String { | ||
format!("Subclasses of `str` should define `__slots__`") | ||
} | ||
} | ||
|
||
/// SLOT000 | ||
pub(crate) fn no_slots_in_str_subclass(checker: &mut Checker, stmt: &Stmt, class: &StmtClassDef) { | ||
if class.bases.iter().any(|base| { | ||
checker | ||
.semantic_model() | ||
.resolve_call_path(base) | ||
.map_or(false, |call_path| { | ||
matches!(call_path.as_slice(), ["" | "builtins", "str"]) | ||
}) | ||
}) { | ||
if !has_slots(&class.body) { | ||
checker.diagnostics.push(Diagnostic::new( | ||
NoSlotsInStrSubclass, | ||
identifier_range(stmt, checker.locator), | ||
)); | ||
} | ||
} | ||
} |
Oops, something went wrong.