mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-27 04:19:18 +00:00
[pylint] Implement useless-with-lock
(#8321)
This commit is contained in:
parent
cda1c5dd35
commit
44e21cfada
8 changed files with 253 additions and 0 deletions
56
crates/ruff_linter/resources/test/fixtures/pylint/useless_with_lock.py
vendored
Normal file
56
crates/ruff_linter/resources/test/fixtures/pylint/useless_with_lock.py
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
import threading
|
||||
from threading import Lock, RLock, Condition, Semaphore, BoundedSemaphore
|
||||
|
||||
|
||||
with threading.Lock(): # [useless-with-lock]
|
||||
...
|
||||
|
||||
with Lock(): # [useless-with-lock]
|
||||
...
|
||||
|
||||
with threading.Lock() as this_shouldnt_matter: # [useless-with-lock]
|
||||
...
|
||||
|
||||
with threading.RLock(): # [useless-with-lock]
|
||||
...
|
||||
|
||||
with RLock(): # [useless-with-lock]
|
||||
...
|
||||
|
||||
with threading.Condition(): # [useless-with-lock]
|
||||
...
|
||||
|
||||
with Condition(): # [useless-with-lock]
|
||||
...
|
||||
|
||||
with threading.Semaphore(): # [useless-with-lock]
|
||||
...
|
||||
|
||||
with Semaphore(): # [useless-with-lock]
|
||||
...
|
||||
|
||||
with threading.BoundedSemaphore(): # [useless-with-lock]
|
||||
...
|
||||
|
||||
with BoundedSemaphore(): # [useless-with-lock]
|
||||
...
|
||||
|
||||
lock = threading.Lock()
|
||||
with lock: # this is ok
|
||||
...
|
||||
|
||||
rlock = threading.RLock()
|
||||
with rlock: # this is ok
|
||||
...
|
||||
|
||||
cond = threading.Condition()
|
||||
with cond: # this is ok
|
||||
...
|
||||
|
||||
sem = threading.Semaphore()
|
||||
with sem: # this is ok
|
||||
...
|
||||
|
||||
b_sem = threading.BoundedSemaphore()
|
||||
with b_sem: # this is ok
|
||||
...
|
|
@ -1186,6 +1186,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
|||
if checker.enabled(Rule::ReadWholeFile) {
|
||||
refurb::rules::read_whole_file(checker, with_stmt);
|
||||
}
|
||||
if checker.enabled(Rule::UselessWithLock) {
|
||||
pylint::rules::useless_with_lock(checker, with_stmt);
|
||||
}
|
||||
}
|
||||
Stmt::While(ast::StmtWhile { body, orelse, .. }) => {
|
||||
if checker.enabled(Rule::FunctionUsesLoopVariable) {
|
||||
|
|
|
@ -277,6 +277,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
|||
(Pylint, "W1514") => (RuleGroup::Preview, rules::pylint::rules::UnspecifiedEncoding),
|
||||
#[allow(deprecated)]
|
||||
(Pylint, "W1641") => (RuleGroup::Nursery, rules::pylint::rules::EqWithoutHash),
|
||||
(Pylint, "W2101") => (RuleGroup::Preview, rules::pylint::rules::UselessWithLock),
|
||||
(Pylint, "R0904") => (RuleGroup::Preview, rules::pylint::rules::TooManyPublicMethods),
|
||||
(Pylint, "W2901") => (RuleGroup::Stable, rules::pylint::rules::RedefinedLoopName),
|
||||
#[allow(deprecated)]
|
||||
|
|
|
@ -115,6 +115,7 @@ mod tests {
|
|||
#[test_case(Rule::UselessElseOnLoop, Path::new("useless_else_on_loop.py"))]
|
||||
#[test_case(Rule::UselessImportAlias, Path::new("import_aliasing.py"))]
|
||||
#[test_case(Rule::UselessReturn, Path::new("useless_return.py"))]
|
||||
#[test_case(Rule::UselessWithLock, Path::new("useless_with_lock.py"))]
|
||||
#[test_case(
|
||||
Rule::YieldFromInAsyncFunction,
|
||||
Path::new("yield_from_in_async_function.py")
|
||||
|
|
|
@ -64,6 +64,7 @@ pub(crate) use unspecified_encoding::*;
|
|||
pub(crate) use useless_else_on_loop::*;
|
||||
pub(crate) use useless_import_alias::*;
|
||||
pub(crate) use useless_return::*;
|
||||
pub(crate) use useless_with_lock::*;
|
||||
pub(crate) use yield_from_in_async_function::*;
|
||||
pub(crate) use yield_in_init::*;
|
||||
|
||||
|
@ -133,5 +134,6 @@ mod unspecified_encoding;
|
|||
mod useless_else_on_loop;
|
||||
mod useless_import_alias;
|
||||
mod useless_return;
|
||||
mod useless_with_lock;
|
||||
mod yield_from_in_async_function;
|
||||
mod yield_in_init;
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for direct uses of lock objects in `with` statements.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Creating a lock (via `threading.Lock` or similar) in a `with` statement
|
||||
/// has no effect, as locks are only relevant when shared between threads.
|
||||
///
|
||||
/// Instead, assign the lock to a variable outside the `with` statement,
|
||||
/// and share that variable between threads.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// import threading
|
||||
///
|
||||
/// counter = 0
|
||||
///
|
||||
///
|
||||
/// def increment():
|
||||
/// global counter
|
||||
///
|
||||
/// with threading.Lock():
|
||||
/// counter += 1
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// import threading
|
||||
///
|
||||
/// counter = 0
|
||||
/// lock = threading.Lock()
|
||||
///
|
||||
///
|
||||
/// def increment():
|
||||
/// global counter
|
||||
///
|
||||
/// with lock:
|
||||
/// counter += 1
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `Lock Objects`](https://docs.python.org/3/library/threading.html#lock-objects)
|
||||
#[violation]
|
||||
pub struct UselessWithLock;
|
||||
|
||||
impl Violation for UselessWithLock {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Threading lock directly created in `with` statement has no effect")
|
||||
}
|
||||
}
|
||||
|
||||
/// PLW2101
|
||||
pub(crate) fn useless_with_lock(checker: &mut Checker, with: &ast::StmtWith) {
|
||||
for item in &with.items {
|
||||
let Some(call) = item.context_expr.as_call_expr() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if !checker
|
||||
.semantic()
|
||||
.resolve_call_path(call.func.as_ref())
|
||||
.is_some_and(|call_path| {
|
||||
matches!(
|
||||
call_path.as_slice(),
|
||||
[
|
||||
"threading",
|
||||
"Lock" | "RLock" | "Condition" | "Semaphore" | "BoundedSemaphore"
|
||||
]
|
||||
)
|
||||
})
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(UselessWithLock, call.range()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pylint/mod.rs
|
||||
---
|
||||
useless_with_lock.py:5:6: PLW2101 Threading lock directly created in `with` statement has no effect
|
||||
|
|
||||
5 | with threading.Lock(): # [useless-with-lock]
|
||||
| ^^^^^^^^^^^^^^^^ PLW2101
|
||||
6 | ...
|
||||
|
|
||||
|
||||
useless_with_lock.py:8:6: PLW2101 Threading lock directly created in `with` statement has no effect
|
||||
|
|
||||
6 | ...
|
||||
7 |
|
||||
8 | with Lock(): # [useless-with-lock]
|
||||
| ^^^^^^ PLW2101
|
||||
9 | ...
|
||||
|
|
||||
|
||||
useless_with_lock.py:11:6: PLW2101 Threading lock directly created in `with` statement has no effect
|
||||
|
|
||||
9 | ...
|
||||
10 |
|
||||
11 | with threading.Lock() as this_shouldnt_matter: # [useless-with-lock]
|
||||
| ^^^^^^^^^^^^^^^^ PLW2101
|
||||
12 | ...
|
||||
|
|
||||
|
||||
useless_with_lock.py:14:6: PLW2101 Threading lock directly created in `with` statement has no effect
|
||||
|
|
||||
12 | ...
|
||||
13 |
|
||||
14 | with threading.RLock(): # [useless-with-lock]
|
||||
| ^^^^^^^^^^^^^^^^^ PLW2101
|
||||
15 | ...
|
||||
|
|
||||
|
||||
useless_with_lock.py:17:6: PLW2101 Threading lock directly created in `with` statement has no effect
|
||||
|
|
||||
15 | ...
|
||||
16 |
|
||||
17 | with RLock(): # [useless-with-lock]
|
||||
| ^^^^^^^ PLW2101
|
||||
18 | ...
|
||||
|
|
||||
|
||||
useless_with_lock.py:20:6: PLW2101 Threading lock directly created in `with` statement has no effect
|
||||
|
|
||||
18 | ...
|
||||
19 |
|
||||
20 | with threading.Condition(): # [useless-with-lock]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ PLW2101
|
||||
21 | ...
|
||||
|
|
||||
|
||||
useless_with_lock.py:23:6: PLW2101 Threading lock directly created in `with` statement has no effect
|
||||
|
|
||||
21 | ...
|
||||
22 |
|
||||
23 | with Condition(): # [useless-with-lock]
|
||||
| ^^^^^^^^^^^ PLW2101
|
||||
24 | ...
|
||||
|
|
||||
|
||||
useless_with_lock.py:26:6: PLW2101 Threading lock directly created in `with` statement has no effect
|
||||
|
|
||||
24 | ...
|
||||
25 |
|
||||
26 | with threading.Semaphore(): # [useless-with-lock]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ PLW2101
|
||||
27 | ...
|
||||
|
|
||||
|
||||
useless_with_lock.py:29:6: PLW2101 Threading lock directly created in `with` statement has no effect
|
||||
|
|
||||
27 | ...
|
||||
28 |
|
||||
29 | with Semaphore(): # [useless-with-lock]
|
||||
| ^^^^^^^^^^^ PLW2101
|
||||
30 | ...
|
||||
|
|
||||
|
||||
useless_with_lock.py:32:6: PLW2101 Threading lock directly created in `with` statement has no effect
|
||||
|
|
||||
30 | ...
|
||||
31 |
|
||||
32 | with threading.BoundedSemaphore(): # [useless-with-lock]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW2101
|
||||
33 | ...
|
||||
|
|
||||
|
||||
useless_with_lock.py:35:6: PLW2101 Threading lock directly created in `with` statement has no effect
|
||||
|
|
||||
33 | ...
|
||||
34 |
|
||||
35 | with BoundedSemaphore(): # [useless-with-lock]
|
||||
| ^^^^^^^^^^^^^^^^^^ PLW2101
|
||||
36 | ...
|
||||
|
|
||||
|
||||
|
3
ruff.schema.json
generated
3
ruff.schema.json
generated
|
@ -3125,6 +3125,9 @@
|
|||
"PLW164",
|
||||
"PLW1641",
|
||||
"PLW2",
|
||||
"PLW21",
|
||||
"PLW210",
|
||||
"PLW2101",
|
||||
"PLW29",
|
||||
"PLW290",
|
||||
"PLW2901",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue