From b9da4305e6ebbd42efa6735e564b77695e2c65cd Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Thu, 21 Nov 2024 16:08:02 +0100 Subject: [PATCH] doc(B024): #14455 add annotated but unassgined class variables (#14502) # Summary Closes #14455, migrated from https://github.com/astral-sh/docs/pull/106. --- .../flake8_bugbear/rules/abstract_base_class.rs | 17 +++++++++++++---- ...es__flake8_bugbear__tests__B024_B024.py.snap | 16 ++++++++-------- scripts/generate_mkdocs.py | 2 +- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs index 0d94b43cedfd1..97d29159eea03 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs @@ -11,12 +11,14 @@ use crate::checkers::ast::Checker; use crate::registry::Rule; /// ## 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? /// 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 -/// method to the class or omitted an `@abstractmethod` decorator. +/// class has no abstract methods or properties, you may have forgotten +/// 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 /// the `ABC` base class from the class definition. @@ -24,9 +26,12 @@ use crate::registry::Rule; /// ## Example /// ```python /// from abc import ABC +/// from typing import ClassVar /// /// /// class Foo(ABC): +/// class_var: ClassVar[str] = "assigned" +/// /// def method(self): /// bar() /// ``` @@ -34,9 +39,12 @@ use crate::registry::Rule; /// Use instead: /// ```python /// from abc import ABC, abstractmethod +/// from typing import ClassVar /// /// /// class Foo(ABC): +/// class_var: ClassVar[str] # unassigned +/// /// @abstractmethod /// def method(self): /// bar() @@ -44,6 +52,7 @@ use crate::registry::Rule; /// /// ## References /// - [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] pub struct AbstractBaseClassWithoutAbstractMethod { name: String, @@ -53,7 +62,7 @@ impl Violation for AbstractBaseClassWithoutAbstractMethod { #[derive_message_formats] fn message(&self) -> String { 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") } } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B024_B024.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B024_B024.py.snap index f78c1cbb988d4..fb1957dd24bf6 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B024_B024.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B024_B024.py.snap @@ -2,7 +2,7 @@ source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs 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 | ^^^^^^ 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() | -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 | ^^^^^^^^^^ B024 @@ -18,7 +18,7 @@ B024.py:71:7: B024 `MetaBase_1` is an abstract base class, but it has no abstrac 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 | ^^^^^^^^^^ 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() | -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 | ^^^^^^^^^^ 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() | -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 | ^^^^^^^^^^^^^ 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() | -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) | ^^^^^^^^^^^^^^^^^^^^^^^^ B024 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) | ^^^^^^^^^^^^^^^^^^^^^^^^ B024 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 141 | class abc_set_class_variable_4(ABC): # error diff --git a/scripts/generate_mkdocs.py b/scripts/generate_mkdocs.py index e2b3a23e4966a..612e3e35232dc 100644 --- a/scripts/generate_mkdocs.py +++ b/scripts/generate_mkdocs.py @@ -110,7 +110,7 @@ def generate_rule_metadata(rule_doc: Path) -> None: For example: ```yaml --- - description: Checks for abstract classes without abstract methods. + description: Checks for abstract classes without abstract methods or properties. tags: - B024 ---