Include static and class methods in in abstract decorator list (#4298)

This commit is contained in:
Charlie Marsh 2023-05-08 21:54:02 -04:00 committed by GitHub
parent f23851130a
commit d365dab904
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 108 additions and 79 deletions

View file

@ -4,7 +4,12 @@ B027 - on lines 13, 16, 19, 23
"""
import abc
from abc import ABC
from abc import abstractmethod, abstractproperty
from abc import (
abstractmethod,
abstractproperty,
abstractclassmethod,
abstractstaticmethod,
)
from abc import abstractmethod as notabstract
from abc import abstractproperty as notabstract_property
@ -55,6 +60,22 @@ class AbstractClass(ABC):
def abstract_6(self):
...
@abstractclassmethod
def abstract_7(self):
pass
@abc.abstractclassmethod
def abstract_8(self):
...
@abstractstaticmethod
def abstract_9(self):
pass
@abc.abstractstaticmethod
def abstract_10(self):
...
def body_1(self):
print("foo")
...

View file

@ -1,96 +1,96 @@
---
source: crates/ruff/src/rules/flake8_bugbear/mod.rs
---
B027.py:13:5: B027 [*] `AbstractClass.empty_1` is an empty method in an abstract base class, but has no abstract decorator
B027.py:18:5: B027 [*] `AbstractClass.empty_1` is an empty method in an abstract base class, but has no abstract decorator
|
13 | class AbstractClass(ABC):
14 | def empty_1(self): # error
18 | class AbstractClass(ABC):
19 | def empty_1(self): # error
| _____^
15 | | ...
20 | | ...
| |___________^ B027
16 |
17 | def empty_2(self): # error
21 |
22 | def empty_2(self): # error
|
= help: Add the `@abstractmethod` decorator
Suggested fix
10 10 |
11 11 |
12 12 | class AbstractClass(ABC):
13 |+ @notabstract
13 14 | def empty_1(self): # error
14 15 | ...
15 16 |
B027.py:16:5: B027 [*] `AbstractClass.empty_2` is an empty method in an abstract base class, but has no abstract decorator
|
16 | ...
17 |
18 | def empty_2(self): # error
| _____^
19 | | pass
| |____________^ B027
20 |
21 | def empty_3(self): # error
|
= help: Add the `@abstractmethod` decorator
Suggested fix
13 13 | def empty_1(self): # error
14 14 | ...
15 15 |
16 |+ @notabstract
16 17 | def empty_2(self): # error
17 18 | pass
18 19 |
16 16 |
17 17 | class AbstractClass(ABC):
18 |+ @notabstract
18 19 | def empty_1(self): # error
19 20 | ...
20 21 |
B027.py:19:5: B027 [*] `AbstractClass.empty_3` is an empty method in an abstract base class, but has no abstract decorator
B027.py:21:5: B027 [*] `AbstractClass.empty_2` is an empty method in an abstract base class, but has no abstract decorator
|
19 | pass
20 |
21 | def empty_3(self): # error
21 | ...
22 |
23 | def empty_2(self): # error
| _____^
22 | | """docstring"""
23 | | ...
| |___________^ B027
24 |
25 | def empty_4(self): # error
|
= help: Add the `@abstractmethod` decorator
Suggested fix
16 16 | def empty_2(self): # error
17 17 | pass
18 18 |
19 |+ @notabstract
19 20 | def empty_3(self): # error
20 21 | """docstring"""
21 22 | ...
B027.py:23:5: B027 [*] `AbstractClass.empty_4` is an empty method in an abstract base class, but has no abstract decorator
|
23 | ...
24 |
25 | def empty_4(self): # error
| _____^
26 | | """multiple ellipsis/pass"""
27 | | ...
28 | | pass
29 | | ...
30 | | pass
24 | | pass
| |____________^ B027
31 |
32 | @notabstract
25 |
26 | def empty_3(self): # error
|
= help: Add the `@abstractmethod` decorator
Suggested fix
20 20 | """docstring"""
21 21 | ...
22 22 |
23 |+ @notabstract
23 24 | def empty_4(self): # error
24 25 | """multiple ellipsis/pass"""
25 26 | ...
18 18 | def empty_1(self): # error
19 19 | ...
20 20 |
21 |+ @notabstract
21 22 | def empty_2(self): # error
22 23 | pass
23 24 |
B027.py:24:5: B027 [*] `AbstractClass.empty_3` is an empty method in an abstract base class, but has no abstract decorator
|
24 | pass
25 |
26 | def empty_3(self): # error
| _____^
27 | | """docstring"""
28 | | ...
| |___________^ B027
29 |
30 | def empty_4(self): # error
|
= help: Add the `@abstractmethod` decorator
Suggested fix
21 21 | def empty_2(self): # error
22 22 | pass
23 23 |
24 |+ @notabstract
24 25 | def empty_3(self): # error
25 26 | """docstring"""
26 27 | ...
B027.py:28:5: B027 [*] `AbstractClass.empty_4` is an empty method in an abstract base class, but has no abstract decorator
|
28 | ...
29 |
30 | def empty_4(self): # error
| _____^
31 | | """multiple ellipsis/pass"""
32 | | ...
33 | | pass
34 | | ...
35 | | pass
| |____________^ B027
36 |
37 | @notabstract
|
= help: Add the `@abstractmethod` decorator
Suggested fix
25 25 | """docstring"""
26 26 | ...
27 27 |
28 |+ @notabstract
28 29 | def empty_4(self): # error
29 30 | """multiple ellipsis/pass"""
30 31 | ...

View file

@ -60,13 +60,21 @@ pub fn is_override(ctx: &Context, decorator_list: &[Expr]) -> bool {
.any(|expr| ctx.match_typing_expr(map_callable(expr), "override"))
}
/// Returns `true` if a function definition is an `@abstractmethod`.
/// Returns `true` if a function definition is an abstract method based on its decorators.
pub fn is_abstract(ctx: &Context, decorator_list: &[Expr]) -> bool {
decorator_list.iter().any(|expr| {
ctx.resolve_call_path(map_callable(expr))
.map_or(false, |call_path| {
call_path.as_slice() == ["abc", "abstractmethod"]
|| call_path.as_slice() == ["abc", "abstractproperty"]
matches!(
call_path.as_slice(),
[
"abc",
"abstractmethod"
| "abstractclassmethod"
| "abstractstaticmethod"
| "abstractproperty"
]
)
})
})
}