mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:44:56 +00:00
[pyupgrade
]: new rule UP050 (useless-class-metaclass-type
) (#18334)
Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
parent
602dd5c039
commit
9ce83c215d
8 changed files with 409 additions and 0 deletions
84
crates/ruff_linter/resources/test/fixtures/pyupgrade/UP050.py
vendored
Normal file
84
crates/ruff_linter/resources/test/fixtures/pyupgrade/UP050.py
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
class A:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class A(metaclass=type):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class A(
|
||||||
|
metaclass=type
|
||||||
|
):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class A(
|
||||||
|
metaclass=type
|
||||||
|
#
|
||||||
|
):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class A(
|
||||||
|
#
|
||||||
|
metaclass=type
|
||||||
|
):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class A(
|
||||||
|
metaclass=type,
|
||||||
|
#
|
||||||
|
):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class A(
|
||||||
|
#
|
||||||
|
metaclass=type,
|
||||||
|
#
|
||||||
|
):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class B(A, metaclass=type):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class B(
|
||||||
|
A,
|
||||||
|
metaclass=type,
|
||||||
|
):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class B(
|
||||||
|
A,
|
||||||
|
# comment
|
||||||
|
metaclass=type,
|
||||||
|
):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def foo():
|
||||||
|
class A(metaclass=type):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class A(
|
||||||
|
metaclass=type # comment
|
||||||
|
,
|
||||||
|
):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
type = str
|
||||||
|
|
||||||
|
class Foo(metaclass=type):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
import builtins
|
||||||
|
|
||||||
|
class A(metaclass=builtins.type):
|
||||||
|
...
|
|
@ -439,6 +439,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||||
if checker.enabled(Rule::UselessObjectInheritance) {
|
if checker.enabled(Rule::UselessObjectInheritance) {
|
||||||
pyupgrade::rules::useless_object_inheritance(checker, class_def);
|
pyupgrade::rules::useless_object_inheritance(checker, class_def);
|
||||||
}
|
}
|
||||||
|
if checker.enabled(Rule::UselessClassMetaclassType) {
|
||||||
|
pyupgrade::rules::useless_class_metaclass_type(checker, class_def);
|
||||||
|
}
|
||||||
if checker.enabled(Rule::ReplaceStrEnum) {
|
if checker.enabled(Rule::ReplaceStrEnum) {
|
||||||
if checker.target_version() >= PythonVersion::PY311 {
|
if checker.target_version() >= PythonVersion::PY311 {
|
||||||
pyupgrade::rules::replace_str_enum(checker, class_def);
|
pyupgrade::rules::replace_str_enum(checker, class_def);
|
||||||
|
|
|
@ -552,6 +552,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||||
(Pyupgrade, "046") => (RuleGroup::Preview, rules::pyupgrade::rules::NonPEP695GenericClass),
|
(Pyupgrade, "046") => (RuleGroup::Preview, rules::pyupgrade::rules::NonPEP695GenericClass),
|
||||||
(Pyupgrade, "047") => (RuleGroup::Preview, rules::pyupgrade::rules::NonPEP695GenericFunction),
|
(Pyupgrade, "047") => (RuleGroup::Preview, rules::pyupgrade::rules::NonPEP695GenericFunction),
|
||||||
(Pyupgrade, "049") => (RuleGroup::Preview, rules::pyupgrade::rules::PrivateTypeParameter),
|
(Pyupgrade, "049") => (RuleGroup::Preview, rules::pyupgrade::rules::PrivateTypeParameter),
|
||||||
|
(Pyupgrade, "050") => (RuleGroup::Preview, rules::pyupgrade::rules::UselessClassMetaclassType),
|
||||||
|
|
||||||
// pydocstyle
|
// pydocstyle
|
||||||
(Pydocstyle, "100") => (RuleGroup::Stable, rules::pydocstyle::rules::UndocumentedPublicModule),
|
(Pydocstyle, "100") => (RuleGroup::Stable, rules::pydocstyle::rules::UndocumentedPublicModule),
|
||||||
|
|
|
@ -111,6 +111,7 @@ mod tests {
|
||||||
#[test_case(Rule::NonPEP695GenericFunction, Path::new("UP047.py"))]
|
#[test_case(Rule::NonPEP695GenericFunction, Path::new("UP047.py"))]
|
||||||
#[test_case(Rule::PrivateTypeParameter, Path::new("UP049_0.py"))]
|
#[test_case(Rule::PrivateTypeParameter, Path::new("UP049_0.py"))]
|
||||||
#[test_case(Rule::PrivateTypeParameter, Path::new("UP049_1.py"))]
|
#[test_case(Rule::PrivateTypeParameter, Path::new("UP049_1.py"))]
|
||||||
|
#[test_case(Rule::UselessClassMetaclassType, Path::new("UP050.py"))]
|
||||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||||
let snapshot = path.to_string_lossy().to_string();
|
let snapshot = path.to_string_lossy().to_string();
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
|
|
|
@ -37,6 +37,7 @@ pub(crate) use unpacked_list_comprehension::*;
|
||||||
pub(crate) use use_pep585_annotation::*;
|
pub(crate) use use_pep585_annotation::*;
|
||||||
pub(crate) use use_pep604_annotation::*;
|
pub(crate) use use_pep604_annotation::*;
|
||||||
pub(crate) use use_pep604_isinstance::*;
|
pub(crate) use use_pep604_isinstance::*;
|
||||||
|
pub(crate) use useless_class_metaclass_type::*;
|
||||||
pub(crate) use useless_metaclass_type::*;
|
pub(crate) use useless_metaclass_type::*;
|
||||||
pub(crate) use useless_object_inheritance::*;
|
pub(crate) use useless_object_inheritance::*;
|
||||||
pub(crate) use yield_in_for_loop::*;
|
pub(crate) use yield_in_for_loop::*;
|
||||||
|
@ -80,6 +81,7 @@ mod unpacked_list_comprehension;
|
||||||
mod use_pep585_annotation;
|
mod use_pep585_annotation;
|
||||||
mod use_pep604_annotation;
|
mod use_pep604_annotation;
|
||||||
mod use_pep604_isinstance;
|
mod use_pep604_isinstance;
|
||||||
|
mod useless_class_metaclass_type;
|
||||||
mod useless_metaclass_type;
|
mod useless_metaclass_type;
|
||||||
mod useless_object_inheritance;
|
mod useless_object_inheritance;
|
||||||
mod yield_in_for_loop;
|
mod yield_in_for_loop;
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
use crate::checkers::ast::Checker;
|
||||||
|
use crate::fix::edits::{Parentheses, remove_argument};
|
||||||
|
use ruff_diagnostics::{Diagnostic, Fix, FixAvailability, Violation};
|
||||||
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
|
use ruff_python_ast::StmtClassDef;
|
||||||
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
/// ## What it does
|
||||||
|
/// Checks for `metaclass=type` in class definitions.
|
||||||
|
///
|
||||||
|
/// ## Why is this bad?
|
||||||
|
/// Since Python 3, the default metaclass is `type`, so specifying it explicitly is redundant.
|
||||||
|
///
|
||||||
|
/// Even though `__prepare__` is not required, the default metaclass (`type`) implements it,
|
||||||
|
/// for the convenience of subclasses calling it via `super()`.
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```python
|
||||||
|
/// class Foo(metaclass=type): ...
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
///
|
||||||
|
/// ```python
|
||||||
|
/// class Foo: ...
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## References
|
||||||
|
/// - [PEP 3115 – Metaclasses in Python 3000](https://peps.python.org/pep-3115/)
|
||||||
|
#[derive(ViolationMetadata)]
|
||||||
|
pub(crate) struct UselessClassMetaclassType {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Violation for UselessClassMetaclassType {
|
||||||
|
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||||
|
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
let UselessClassMetaclassType { name } = self;
|
||||||
|
format!("Class `{name}` uses `metaclass=type`, which is redundant")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fix_title(&self) -> Option<String> {
|
||||||
|
Some("Remove `metaclass=type`".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UP050
|
||||||
|
pub(crate) fn useless_class_metaclass_type(checker: &Checker, class_def: &StmtClassDef) {
|
||||||
|
let Some(arguments) = class_def.arguments.as_deref() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
for keyword in &arguments.keywords {
|
||||||
|
if let (Some("metaclass"), expr) = (keyword.arg.as_deref(), &keyword.value) {
|
||||||
|
if checker.semantic().match_builtin_expr(expr, "type") {
|
||||||
|
let mut diagnostic = Diagnostic::new(
|
||||||
|
UselessClassMetaclassType {
|
||||||
|
name: class_def.name.to_string(),
|
||||||
|
},
|
||||||
|
keyword.range(),
|
||||||
|
);
|
||||||
|
|
||||||
|
diagnostic.try_set_fix(|| {
|
||||||
|
remove_argument(
|
||||||
|
keyword,
|
||||||
|
arguments,
|
||||||
|
Parentheses::Remove,
|
||||||
|
checker.locator().contents(),
|
||||||
|
)
|
||||||
|
.map(Fix::safe_edit)
|
||||||
|
});
|
||||||
|
|
||||||
|
checker.report_diagnostic(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,237 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||||
|
---
|
||||||
|
UP050.py:5:9: UP050 [*] Class `A` uses `metaclass=type`, which is redundant
|
||||||
|
|
|
||||||
|
5 | class A(metaclass=type):
|
||||||
|
| ^^^^^^^^^^^^^^ UP050
|
||||||
|
6 | ...
|
||||||
|
|
|
||||||
|
= help: Remove `metaclass=type`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
2 2 | ...
|
||||||
|
3 3 |
|
||||||
|
4 4 |
|
||||||
|
5 |-class A(metaclass=type):
|
||||||
|
5 |+class A:
|
||||||
|
6 6 | ...
|
||||||
|
7 7 |
|
||||||
|
8 8 |
|
||||||
|
|
||||||
|
UP050.py:10:5: UP050 [*] Class `A` uses `metaclass=type`, which is redundant
|
||||||
|
|
|
||||||
|
9 | class A(
|
||||||
|
10 | metaclass=type
|
||||||
|
| ^^^^^^^^^^^^^^ UP050
|
||||||
|
11 | ):
|
||||||
|
12 | ...
|
||||||
|
|
|
||||||
|
= help: Remove `metaclass=type`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
6 6 | ...
|
||||||
|
7 7 |
|
||||||
|
8 8 |
|
||||||
|
9 |-class A(
|
||||||
|
10 |- metaclass=type
|
||||||
|
11 |-):
|
||||||
|
9 |+class A:
|
||||||
|
12 10 | ...
|
||||||
|
13 11 |
|
||||||
|
14 12 |
|
||||||
|
|
||||||
|
UP050.py:16:5: UP050 [*] Class `A` uses `metaclass=type`, which is redundant
|
||||||
|
|
|
||||||
|
15 | class A(
|
||||||
|
16 | metaclass=type
|
||||||
|
| ^^^^^^^^^^^^^^ UP050
|
||||||
|
17 | #
|
||||||
|
18 | ):
|
||||||
|
|
|
||||||
|
= help: Remove `metaclass=type`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
12 12 | ...
|
||||||
|
13 13 |
|
||||||
|
14 14 |
|
||||||
|
15 |-class A(
|
||||||
|
16 |- metaclass=type
|
||||||
|
17 |- #
|
||||||
|
18 |-):
|
||||||
|
15 |+class A:
|
||||||
|
19 16 | ...
|
||||||
|
20 17 |
|
||||||
|
21 18 |
|
||||||
|
|
||||||
|
UP050.py:24:5: UP050 [*] Class `A` uses `metaclass=type`, which is redundant
|
||||||
|
|
|
||||||
|
22 | class A(
|
||||||
|
23 | #
|
||||||
|
24 | metaclass=type
|
||||||
|
| ^^^^^^^^^^^^^^ UP050
|
||||||
|
25 | ):
|
||||||
|
26 | ...
|
||||||
|
|
|
||||||
|
= help: Remove `metaclass=type`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
19 19 | ...
|
||||||
|
20 20 |
|
||||||
|
21 21 |
|
||||||
|
22 |-class A(
|
||||||
|
23 |- #
|
||||||
|
24 |- metaclass=type
|
||||||
|
25 |-):
|
||||||
|
22 |+class A:
|
||||||
|
26 23 | ...
|
||||||
|
27 24 |
|
||||||
|
28 25 |
|
||||||
|
|
||||||
|
UP050.py:30:5: UP050 [*] Class `A` uses `metaclass=type`, which is redundant
|
||||||
|
|
|
||||||
|
29 | class A(
|
||||||
|
30 | metaclass=type,
|
||||||
|
| ^^^^^^^^^^^^^^ UP050
|
||||||
|
31 | #
|
||||||
|
32 | ):
|
||||||
|
|
|
||||||
|
= help: Remove `metaclass=type`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
26 26 | ...
|
||||||
|
27 27 |
|
||||||
|
28 28 |
|
||||||
|
29 |-class A(
|
||||||
|
30 |- metaclass=type,
|
||||||
|
31 |- #
|
||||||
|
32 |-):
|
||||||
|
29 |+class A:
|
||||||
|
33 30 | ...
|
||||||
|
34 31 |
|
||||||
|
35 32 |
|
||||||
|
|
||||||
|
UP050.py:38:5: UP050 [*] Class `A` uses `metaclass=type`, which is redundant
|
||||||
|
|
|
||||||
|
36 | class A(
|
||||||
|
37 | #
|
||||||
|
38 | metaclass=type,
|
||||||
|
| ^^^^^^^^^^^^^^ UP050
|
||||||
|
39 | #
|
||||||
|
40 | ):
|
||||||
|
|
|
||||||
|
= help: Remove `metaclass=type`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
33 33 | ...
|
||||||
|
34 34 |
|
||||||
|
35 35 |
|
||||||
|
36 |-class A(
|
||||||
|
37 |- #
|
||||||
|
38 |- metaclass=type,
|
||||||
|
39 |- #
|
||||||
|
40 |-):
|
||||||
|
36 |+class A:
|
||||||
|
41 37 | ...
|
||||||
|
42 38 |
|
||||||
|
43 39 |
|
||||||
|
|
||||||
|
UP050.py:44:12: UP050 [*] Class `B` uses `metaclass=type`, which is redundant
|
||||||
|
|
|
||||||
|
44 | class B(A, metaclass=type):
|
||||||
|
| ^^^^^^^^^^^^^^ UP050
|
||||||
|
45 | ...
|
||||||
|
|
|
||||||
|
= help: Remove `metaclass=type`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
41 41 | ...
|
||||||
|
42 42 |
|
||||||
|
43 43 |
|
||||||
|
44 |-class B(A, metaclass=type):
|
||||||
|
44 |+class B(A):
|
||||||
|
45 45 | ...
|
||||||
|
46 46 |
|
||||||
|
47 47 |
|
||||||
|
|
||||||
|
UP050.py:50:5: UP050 [*] Class `B` uses `metaclass=type`, which is redundant
|
||||||
|
|
|
||||||
|
48 | class B(
|
||||||
|
49 | A,
|
||||||
|
50 | metaclass=type,
|
||||||
|
| ^^^^^^^^^^^^^^ UP050
|
||||||
|
51 | ):
|
||||||
|
52 | ...
|
||||||
|
|
|
||||||
|
= help: Remove `metaclass=type`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
47 47 |
|
||||||
|
48 48 | class B(
|
||||||
|
49 49 | A,
|
||||||
|
50 |- metaclass=type,
|
||||||
|
51 50 | ):
|
||||||
|
52 51 | ...
|
||||||
|
53 52 |
|
||||||
|
|
||||||
|
UP050.py:58:5: UP050 [*] Class `B` uses `metaclass=type`, which is redundant
|
||||||
|
|
|
||||||
|
56 | A,
|
||||||
|
57 | # comment
|
||||||
|
58 | metaclass=type,
|
||||||
|
| ^^^^^^^^^^^^^^ UP050
|
||||||
|
59 | ):
|
||||||
|
60 | ...
|
||||||
|
|
|
||||||
|
= help: Remove `metaclass=type`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
54 54 |
|
||||||
|
55 55 | class B(
|
||||||
|
56 56 | A,
|
||||||
|
57 |- # comment
|
||||||
|
58 |- metaclass=type,
|
||||||
|
59 57 | ):
|
||||||
|
60 58 | ...
|
||||||
|
61 59 |
|
||||||
|
|
||||||
|
UP050.py:69:5: UP050 [*] Class `A` uses `metaclass=type`, which is redundant
|
||||||
|
|
|
||||||
|
68 | class A(
|
||||||
|
69 | metaclass=type # comment
|
||||||
|
| ^^^^^^^^^^^^^^ UP050
|
||||||
|
70 | ,
|
||||||
|
71 | ):
|
||||||
|
|
|
||||||
|
= help: Remove `metaclass=type`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
65 65 | ...
|
||||||
|
66 66 |
|
||||||
|
67 67 |
|
||||||
|
68 |-class A(
|
||||||
|
69 |- metaclass=type # comment
|
||||||
|
70 |- ,
|
||||||
|
71 |-):
|
||||||
|
68 |+class A:
|
||||||
|
72 69 | ...
|
||||||
|
73 70 |
|
||||||
|
74 71 |
|
||||||
|
|
||||||
|
UP050.py:83:9: UP050 [*] Class `A` uses `metaclass=type`, which is redundant
|
||||||
|
|
|
||||||
|
81 | import builtins
|
||||||
|
82 |
|
||||||
|
83 | class A(metaclass=builtins.type):
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ UP050
|
||||||
|
84 | ...
|
||||||
|
|
|
||||||
|
= help: Remove `metaclass=type`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
80 80 |
|
||||||
|
81 81 | import builtins
|
||||||
|
82 82 |
|
||||||
|
83 |-class A(metaclass=builtins.type):
|
||||||
|
83 |+class A:
|
||||||
|
84 84 | ...
|
2
ruff.schema.json
generated
2
ruff.schema.json
generated
|
@ -4299,6 +4299,8 @@
|
||||||
"UP046",
|
"UP046",
|
||||||
"UP047",
|
"UP047",
|
||||||
"UP049",
|
"UP049",
|
||||||
|
"UP05",
|
||||||
|
"UP050",
|
||||||
"W",
|
"W",
|
||||||
"W1",
|
"W1",
|
||||||
"W19",
|
"W19",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue