mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-03 15:14:42 +00:00
[flake8-bugbear
] Mark the fix for unreliable-callable-check
as always unsafe (B004
) (#20318)
## Summary Resolves #20282 Makes the rule fix always unsafe, because the replacement may not be semantically equivalent to the original expression, potentially changing the behavior of the code. Updated docstring with examples. ## Test Plan - Added two tests from issue and regenerated the snapshot --------- Co-authored-by: Igor Drokin <drokinii1017@gmail.com> Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
This commit is contained in:
parent
ff677a96e4
commit
dfec94608c
3 changed files with 80 additions and 8 deletions
|
@ -39,3 +39,16 @@ hasattr(
|
||||||
"__call__", # comment 4
|
"__call__", # comment 4
|
||||||
# comment 5
|
# comment 5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import operator
|
||||||
|
|
||||||
|
assert hasattr(operator, "__call__")
|
||||||
|
assert callable(operator) is False
|
||||||
|
|
||||||
|
|
||||||
|
class A:
|
||||||
|
def __init__(self): self.__call__ = None
|
||||||
|
|
||||||
|
|
||||||
|
assert hasattr(A(), "__call__")
|
||||||
|
assert callable(A()) is False
|
||||||
|
|
|
@ -28,10 +28,29 @@ use crate::{Edit, Fix, FixAvailability, Violation};
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ## Fix safety
|
/// ## Fix safety
|
||||||
/// This rule's fix is marked as unsafe if there's comments in the `hasattr` call
|
/// This rule's fix is marked as unsafe because the replacement may not be semantically
|
||||||
/// expression, as comments may be removed.
|
/// equivalent to the original expression, potentially changing the behavior of the code.
|
||||||
///
|
///
|
||||||
/// For example, the fix would be marked as unsafe in the following case:
|
/// For example, an imported module may have a `__call__` attribute but is not considered
|
||||||
|
/// a callable object:
|
||||||
|
/// ```python
|
||||||
|
/// import operator
|
||||||
|
///
|
||||||
|
/// assert hasattr(operator, "__call__")
|
||||||
|
/// assert callable(operator) is False
|
||||||
|
/// ```
|
||||||
|
/// Additionally, `__call__` may be defined only as an instance method:
|
||||||
|
/// ```python
|
||||||
|
/// class A:
|
||||||
|
/// def __init__(self):
|
||||||
|
/// self.__call__ = None
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// assert hasattr(A(), "__call__")
|
||||||
|
/// assert callable(A()) is False
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Additionally, if there are comments in the `hasattr` call expression, they may be removed:
|
||||||
/// ```python
|
/// ```python
|
||||||
/// hasattr(
|
/// hasattr(
|
||||||
/// # comment 1
|
/// # comment 1
|
||||||
|
@ -103,11 +122,7 @@ pub(crate) fn unreliable_callable_check(
|
||||||
Ok(Fix::applicable_edits(
|
Ok(Fix::applicable_edits(
|
||||||
binding_edit,
|
binding_edit,
|
||||||
import_edit,
|
import_edit,
|
||||||
if checker.comment_ranges().intersects(expr.range()) {
|
Applicability::Unsafe,
|
||||||
Applicability::Unsafe
|
|
||||||
} else {
|
|
||||||
Applicability::Safe
|
|
||||||
},
|
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ help: Replace with `callable()`
|
||||||
4 | print("Ooh, callable! Or is it?")
|
4 | print("Ooh, callable! Or is it?")
|
||||||
5 | if getattr(o, "__call__", False):
|
5 | if getattr(o, "__call__", False):
|
||||||
6 | print("Ooh, callable! Or is it?")
|
6 | print("Ooh, callable! Or is it?")
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B004 Using `hasattr(x, "__call__")` to test if x is callable is unreliable. Use `callable(x)` for consistent results.
|
B004 Using `hasattr(x, "__call__")` to test if x is callable is unreliable. Use `callable(x)` for consistent results.
|
||||||
--> B004.py:5:8
|
--> B004.py:5:8
|
||||||
|
@ -50,6 +51,7 @@ help: Replace with `callable()`
|
||||||
13 | print("B U G")
|
13 | print("B U G")
|
||||||
14 | if builtins.getattr(o, "__call__", False):
|
14 | if builtins.getattr(o, "__call__", False):
|
||||||
15 | print("B U G")
|
15 | print("B U G")
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B004 Using `hasattr(x, "__call__")` to test if x is callable is unreliable. Use `callable(x)` for consistent results.
|
B004 Using `hasattr(x, "__call__")` to test if x is callable is unreliable. Use `callable(x)` for consistent results.
|
||||||
--> B004.py:14:8
|
--> B004.py:14:8
|
||||||
|
@ -85,6 +87,7 @@ help: Replace with `callable()`
|
||||||
26 | print("STILL a bug!")
|
26 | print("STILL a bug!")
|
||||||
27 |
|
27 |
|
||||||
28 |
|
28 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B004 [*] Using `hasattr(x, "__call__")` to test if x is callable is unreliable. Use `callable(x)` for consistent results.
|
B004 [*] Using `hasattr(x, "__call__")` to test if x is callable is unreliable. Use `callable(x)` for consistent results.
|
||||||
--> B004.py:35:1
|
--> B004.py:35:1
|
||||||
|
@ -99,6 +102,8 @@ B004 [*] Using `hasattr(x, "__call__")` to test if x is callable is unreliable.
|
||||||
40 | | # comment 5
|
40 | | # comment 5
|
||||||
41 | | )
|
41 | | )
|
||||||
| |_^
|
| |_^
|
||||||
|
42 |
|
||||||
|
43 | import operator
|
||||||
|
|
|
|
||||||
help: Replace with `callable()`
|
help: Replace with `callable()`
|
||||||
32 |
|
32 |
|
||||||
|
@ -112,4 +117,43 @@ help: Replace with `callable()`
|
||||||
- # comment 5
|
- # comment 5
|
||||||
- )
|
- )
|
||||||
35 + callable(obj)
|
35 + callable(obj)
|
||||||
|
36 |
|
||||||
|
37 | import operator
|
||||||
|
38 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
B004 [*] Using `hasattr(x, "__call__")` to test if x is callable is unreliable. Use `callable(x)` for consistent results.
|
||||||
|
--> B004.py:45:8
|
||||||
|
|
|
||||||
|
43 | import operator
|
||||||
|
44 |
|
||||||
|
45 | assert hasattr(operator, "__call__")
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
46 | assert callable(operator) is False
|
||||||
|
|
|
||||||
|
help: Replace with `callable()`
|
||||||
|
42 |
|
||||||
|
43 | import operator
|
||||||
|
44 |
|
||||||
|
- assert hasattr(operator, "__call__")
|
||||||
|
45 + assert callable(operator)
|
||||||
|
46 | assert callable(operator) is False
|
||||||
|
47 |
|
||||||
|
48 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
B004 [*] Using `hasattr(x, "__call__")` to test if x is callable is unreliable. Use `callable(x)` for consistent results.
|
||||||
|
--> B004.py:53:8
|
||||||
|
|
|
||||||
|
53 | assert hasattr(A(), "__call__")
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
54 | assert callable(A()) is False
|
||||||
|
|
|
||||||
|
help: Replace with `callable()`
|
||||||
|
50 | def __init__(self): self.__call__ = None
|
||||||
|
51 |
|
||||||
|
52 |
|
||||||
|
- assert hasattr(A(), "__call__")
|
||||||
|
53 + assert callable(A())
|
||||||
|
54 | assert callable(A()) is False
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue