mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-09 13:18:52 +00:00
Include static and class methods in in abstract decorator list (#4298)
This commit is contained in:
parent
f23851130a
commit
d365dab904
3 changed files with 108 additions and 79 deletions
|
@ -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")
|
||||
...
|
||||
|
|
|
@ -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 | ...
|
||||
|
||||
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue