doc(B024): #14455 add annotated but unassgined class variables (#14502)

# Summary

Closes #14455, migrated from https://github.com/astral-sh/docs/pull/106.
This commit is contained in:
cmp0xff 2024-11-21 16:08:02 +01:00 committed by GitHub
parent 87043a2415
commit b9da4305e6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 22 additions and 13 deletions

View file

@ -11,12 +11,14 @@ use crate::checkers::ast::Checker;
use crate::registry::Rule; use crate::registry::Rule;
/// ## What it does /// ## What it does
/// Checks for abstract classes without abstract methods. /// Checks for abstract classes without abstract methods or properties.
/// Annotated but unassigned class variables are regarded as abstract.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// Abstract base classes are used to define interfaces. If an abstract base /// Abstract base classes are used to define interfaces. If an abstract base
/// class has no abstract methods, you may have forgotten to add an abstract /// class has no abstract methods or properties, you may have forgotten
/// method to the class or omitted an `@abstractmethod` decorator. /// to add an abstract method or property to the class,
/// or omitted an `@abstractmethod` decorator.
/// ///
/// If the class is _not_ meant to be used as an interface, consider removing /// If the class is _not_ meant to be used as an interface, consider removing
/// the `ABC` base class from the class definition. /// the `ABC` base class from the class definition.
@ -24,9 +26,12 @@ use crate::registry::Rule;
/// ## Example /// ## Example
/// ```python /// ```python
/// from abc import ABC /// from abc import ABC
/// from typing import ClassVar
/// ///
/// ///
/// class Foo(ABC): /// class Foo(ABC):
/// class_var: ClassVar[str] = "assigned"
///
/// def method(self): /// def method(self):
/// bar() /// bar()
/// ``` /// ```
@ -34,9 +39,12 @@ use crate::registry::Rule;
/// Use instead: /// Use instead:
/// ```python /// ```python
/// from abc import ABC, abstractmethod /// from abc import ABC, abstractmethod
/// from typing import ClassVar
/// ///
/// ///
/// class Foo(ABC): /// class Foo(ABC):
/// class_var: ClassVar[str] # unassigned
///
/// @abstractmethod /// @abstractmethod
/// def method(self): /// def method(self):
/// bar() /// bar()
@ -44,6 +52,7 @@ use crate::registry::Rule;
/// ///
/// ## References /// ## References
/// - [Python documentation: `abc`](https://docs.python.org/3/library/abc.html) /// - [Python documentation: `abc`](https://docs.python.org/3/library/abc.html)
/// - [Python documentation: `typing.ClassVar`](https://docs.python.org/3/library/typing.html#typing.ClassVar)
#[violation] #[violation]
pub struct AbstractBaseClassWithoutAbstractMethod { pub struct AbstractBaseClassWithoutAbstractMethod {
name: String, name: String,
@ -53,7 +62,7 @@ impl Violation for AbstractBaseClassWithoutAbstractMethod {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let AbstractBaseClassWithoutAbstractMethod { name } = self; let AbstractBaseClassWithoutAbstractMethod { name } = self;
format!("`{name}` is an abstract base class, but it has no abstract methods") format!("`{name}` is an abstract base class, but it has no abstract methods or properties")
} }
} }

View file

@ -2,7 +2,7 @@
source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs
snapshot_kind: text snapshot_kind: text
--- ---
B024.py:18:7: B024 `Base_1` is an abstract base class, but it has no abstract methods B024.py:18:7: B024 `Base_1` is an abstract base class, but it has no abstract methods or properties
| |
18 | class Base_1(ABC): # error 18 | class Base_1(ABC): # error
| ^^^^^^ B024 | ^^^^^^ B024
@ -10,7 +10,7 @@ B024.py:18:7: B024 `Base_1` is an abstract base class, but it has no abstract me
20 | foo() 20 | foo()
| |
B024.py:71:7: B024 `MetaBase_1` is an abstract base class, but it has no abstract methods B024.py:71:7: B024 `MetaBase_1` is an abstract base class, but it has no abstract methods or properties
| |
71 | class MetaBase_1(metaclass=ABCMeta): # error 71 | class MetaBase_1(metaclass=ABCMeta): # error
| ^^^^^^^^^^ B024 | ^^^^^^^^^^ B024
@ -18,7 +18,7 @@ B024.py:71:7: B024 `MetaBase_1` is an abstract base class, but it has no abstrac
73 | foo() 73 | foo()
| |
B024.py:82:7: B024 `abc_Base_1` is an abstract base class, but it has no abstract methods B024.py:82:7: B024 `abc_Base_1` is an abstract base class, but it has no abstract methods or properties
| |
82 | class abc_Base_1(abc.ABC): # error 82 | class abc_Base_1(abc.ABC): # error
| ^^^^^^^^^^ B024 | ^^^^^^^^^^ B024
@ -26,7 +26,7 @@ B024.py:82:7: B024 `abc_Base_1` is an abstract base class, but it has no abstrac
84 | foo() 84 | foo()
| |
B024.py:87:7: B024 `abc_Base_2` is an abstract base class, but it has no abstract methods B024.py:87:7: B024 `abc_Base_2` is an abstract base class, but it has no abstract methods or properties
| |
87 | class abc_Base_2(metaclass=abc.ABCMeta): # error 87 | class abc_Base_2(metaclass=abc.ABCMeta): # error
| ^^^^^^^^^^ B024 | ^^^^^^^^^^ B024
@ -34,7 +34,7 @@ B024.py:87:7: B024 `abc_Base_2` is an abstract base class, but it has no abstrac
89 | foo() 89 | foo()
| |
B024.py:92:7: B024 `notabc_Base_1` is an abstract base class, but it has no abstract methods B024.py:92:7: B024 `notabc_Base_1` is an abstract base class, but it has no abstract methods or properties
| |
92 | class notabc_Base_1(notabc.ABC): # error 92 | class notabc_Base_1(notabc.ABC): # error
| ^^^^^^^^^^^^^ B024 | ^^^^^^^^^^^^^ B024
@ -42,21 +42,21 @@ B024.py:92:7: B024 `notabc_Base_1` is an abstract base class, but it has no abst
94 | foo() 94 | foo()
| |
B024.py:132:7: B024 `abc_set_class_variable_2` is an abstract base class, but it has no abstract methods B024.py:132:7: B024 `abc_set_class_variable_2` is an abstract base class, but it has no abstract methods or properties
| |
132 | class abc_set_class_variable_2(ABC): # error (not an abstract attribute) 132 | class abc_set_class_variable_2(ABC): # error (not an abstract attribute)
| ^^^^^^^^^^^^^^^^^^^^^^^^ B024 | ^^^^^^^^^^^^^^^^^^^^^^^^ B024
133 | foo = 2 133 | foo = 2
| |
B024.py:136:7: B024 `abc_set_class_variable_3` is an abstract base class, but it has no abstract methods B024.py:136:7: B024 `abc_set_class_variable_3` is an abstract base class, but it has no abstract methods or properties
| |
136 | class abc_set_class_variable_3(ABC): # error (not an abstract attribute) 136 | class abc_set_class_variable_3(ABC): # error (not an abstract attribute)
| ^^^^^^^^^^^^^^^^^^^^^^^^ B024 | ^^^^^^^^^^^^^^^^^^^^^^^^ B024
137 | foo: int = 2 137 | foo: int = 2
| |
B024.py:141:7: B024 `abc_set_class_variable_4` is an abstract base class, but it has no abstract methods B024.py:141:7: B024 `abc_set_class_variable_4` is an abstract base class, but it has no abstract methods or properties
| |
140 | # this doesn't actually declare a class variable, it's just an expression 140 | # this doesn't actually declare a class variable, it's just an expression
141 | class abc_set_class_variable_4(ABC): # error 141 | class abc_set_class_variable_4(ABC): # error

View file

@ -110,7 +110,7 @@ def generate_rule_metadata(rule_doc: Path) -> None:
For example: For example:
```yaml ```yaml
--- ---
description: Checks for abstract classes without abstract methods. description: Checks for abstract classes without abstract methods or properties.
tags: tags:
- B024 - B024
--- ---