mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:24 +00:00
ignore if using infinite iterators in B905
(#4914)
This commit is contained in:
parent
ac4a4da50e
commit
01d3d4bbd2
4 changed files with 133 additions and 55 deletions
|
@ -1,3 +1,6 @@
|
|||
from itertools import count, cycle, repeat
|
||||
|
||||
# Errors
|
||||
zip()
|
||||
zip(range(3))
|
||||
zip("a", "b")
|
||||
|
@ -5,6 +8,18 @@ zip("a", "b", *zip("c"))
|
|||
zip(zip("a"), strict=False)
|
||||
zip(zip("a", strict=True))
|
||||
|
||||
# OK
|
||||
zip(range(3), strict=True)
|
||||
zip("a", "b", strict=False)
|
||||
zip("a", "b", "c", strict=True)
|
||||
|
||||
# OK (infinite iterators).
|
||||
zip([1, 2, 3], cycle("ABCDEF"))
|
||||
zip([1, 2, 3], count())
|
||||
zip([1, 2, 3], repeat(1))
|
||||
zip([1, 2, 3], repeat(1, None))
|
||||
zip([1, 2, 3], repeat(1, times=None))
|
||||
|
||||
# Errors (limited iterators).
|
||||
zip([1, 2, 3], repeat(1, 1))
|
||||
zip([1, 2, 3], repeat(1, times=4))
|
||||
|
|
|
@ -2633,7 +2633,9 @@ where
|
|||
if self.enabled(Rule::ZipWithoutExplicitStrict)
|
||||
&& self.settings.target_version >= PythonVersion::Py310
|
||||
{
|
||||
flake8_bugbear::rules::zip_without_explicit_strict(self, expr, func, keywords);
|
||||
flake8_bugbear::rules::zip_without_explicit_strict(
|
||||
self, expr, func, args, keywords,
|
||||
);
|
||||
}
|
||||
if self.enabled(Rule::NoExplicitStacklevel) {
|
||||
flake8_bugbear::rules::no_explicit_stacklevel(self, func, args, keywords);
|
||||
|
|
|
@ -2,6 +2,8 @@ use rustpython_parser::ast::{self, Expr, Keyword, Ranged};
|
|||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::is_const_none;
|
||||
use ruff_python_semantic::model::SemanticModel;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
|
@ -20,6 +22,7 @@ pub(crate) fn zip_without_explicit_strict(
|
|||
checker: &mut Checker,
|
||||
expr: &Expr,
|
||||
func: &Expr,
|
||||
args: &[Expr],
|
||||
kwargs: &[Keyword],
|
||||
) {
|
||||
if let Expr::Name(ast::ExprName { id, .. }) = func {
|
||||
|
@ -28,6 +31,9 @@ pub(crate) fn zip_without_explicit_strict(
|
|||
&& !kwargs
|
||||
.iter()
|
||||
.any(|keyword| keyword.arg.as_ref().map_or(false, |name| name == "strict"))
|
||||
&& !args
|
||||
.iter()
|
||||
.any(|arg| is_infinite_iterator(arg, checker.semantic_model()))
|
||||
{
|
||||
checker
|
||||
.diagnostics
|
||||
|
@ -35,3 +41,40 @@ pub(crate) fn zip_without_explicit_strict(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if the [`Expr`] appears to be an infinite iterator (e.g., a call to
|
||||
/// `itertools.cycle` or similar).
|
||||
fn is_infinite_iterator(arg: &Expr, model: &SemanticModel) -> bool {
|
||||
let Expr::Call(ast::ExprCall { func, args, keywords, .. }) = &arg else {
|
||||
return false;
|
||||
};
|
||||
|
||||
return model
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| match call_path.as_slice() {
|
||||
["itertools", "cycle" | "count"] => true,
|
||||
["itertools", "repeat"] => {
|
||||
// Ex) `itertools.repeat(1)`
|
||||
if keywords.is_empty() && args.len() == 1 {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ex) `itertools.repeat(1, None)`
|
||||
if args.len() == 2 && is_const_none(&args[1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ex) `iterools.repeat(1, times=None)`
|
||||
for keyword in keywords {
|
||||
if keyword.arg.as_ref().map_or(false, |name| name == "times") {
|
||||
if is_const_none(&keyword.value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,70 +1,88 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_bugbear/mod.rs
|
||||
---
|
||||
B905.py:1:1: B905 `zip()` without an explicit `strict=` parameter
|
||||
|
|
||||
1 | zip()
|
||||
| ^^^^^ B905
|
||||
2 | zip(range(3))
|
||||
3 | zip("a", "b")
|
||||
|
|
||||
|
||||
B905.py:2:1: B905 `zip()` without an explicit `strict=` parameter
|
||||
|
|
||||
2 | zip()
|
||||
3 | zip(range(3))
|
||||
| ^^^^^^^^^^^^^ B905
|
||||
4 | zip("a", "b")
|
||||
5 | zip("a", "b", *zip("c"))
|
||||
|
|
||||
|
||||
B905.py:3:1: B905 `zip()` without an explicit `strict=` parameter
|
||||
|
|
||||
3 | zip()
|
||||
4 | zip(range(3))
|
||||
5 | zip("a", "b")
|
||||
| ^^^^^^^^^^^^^ B905
|
||||
6 | zip("a", "b", *zip("c"))
|
||||
7 | zip(zip("a"), strict=False)
|
||||
|
|
||||
|
||||
B905.py:4:1: B905 `zip()` without an explicit `strict=` parameter
|
||||
|
|
||||
4 | zip(range(3))
|
||||
5 | zip("a", "b")
|
||||
6 | zip("a", "b", *zip("c"))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ B905
|
||||
7 | zip(zip("a"), strict=False)
|
||||
8 | zip(zip("a", strict=True))
|
||||
4 | # Errors
|
||||
5 | zip()
|
||||
| ^^^^^ B905
|
||||
6 | zip(range(3))
|
||||
7 | zip("a", "b")
|
||||
|
|
||||
|
||||
B905.py:4:16: B905 `zip()` without an explicit `strict=` parameter
|
||||
B905.py:5:1: B905 `zip()` without an explicit `strict=` parameter
|
||||
|
|
||||
4 | zip(range(3))
|
||||
5 | zip("a", "b")
|
||||
6 | zip("a", "b", *zip("c"))
|
||||
| ^^^^^^^^ B905
|
||||
7 | zip(zip("a"), strict=False)
|
||||
8 | zip(zip("a", strict=True))
|
||||
|
|
||||
|
||||
B905.py:5:5: B905 `zip()` without an explicit `strict=` parameter
|
||||
|
|
||||
5 | zip("a", "b")
|
||||
6 | zip("a", "b", *zip("c"))
|
||||
7 | zip(zip("a"), strict=False)
|
||||
| ^^^^^^^^ B905
|
||||
8 | zip(zip("a", strict=True))
|
||||
5 | # Errors
|
||||
6 | zip()
|
||||
7 | zip(range(3))
|
||||
| ^^^^^^^^^^^^^ B905
|
||||
8 | zip("a", "b")
|
||||
9 | zip("a", "b", *zip("c"))
|
||||
|
|
||||
|
||||
B905.py:6:1: B905 `zip()` without an explicit `strict=` parameter
|
||||
|
|
||||
6 | zip("a", "b", *zip("c"))
|
||||
7 | zip(zip("a"), strict=False)
|
||||
8 | zip(zip("a", strict=True))
|
||||
6 | zip()
|
||||
7 | zip(range(3))
|
||||
8 | zip("a", "b")
|
||||
| ^^^^^^^^^^^^^ B905
|
||||
9 | zip("a", "b", *zip("c"))
|
||||
10 | zip(zip("a"), strict=False)
|
||||
|
|
||||
|
||||
B905.py:7:1: B905 `zip()` without an explicit `strict=` parameter
|
||||
|
|
||||
7 | zip(range(3))
|
||||
8 | zip("a", "b")
|
||||
9 | zip("a", "b", *zip("c"))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ B905
|
||||
10 | zip(zip("a"), strict=False)
|
||||
11 | zip(zip("a", strict=True))
|
||||
|
|
||||
|
||||
B905.py:7:16: B905 `zip()` without an explicit `strict=` parameter
|
||||
|
|
||||
7 | zip(range(3))
|
||||
8 | zip("a", "b")
|
||||
9 | zip("a", "b", *zip("c"))
|
||||
| ^^^^^^^^ B905
|
||||
10 | zip(zip("a"), strict=False)
|
||||
11 | zip(zip("a", strict=True))
|
||||
|
|
||||
|
||||
B905.py:8:5: B905 `zip()` without an explicit `strict=` parameter
|
||||
|
|
||||
8 | zip("a", "b")
|
||||
9 | zip("a", "b", *zip("c"))
|
||||
10 | zip(zip("a"), strict=False)
|
||||
| ^^^^^^^^ B905
|
||||
11 | zip(zip("a", strict=True))
|
||||
|
|
||||
|
||||
B905.py:9:1: B905 `zip()` without an explicit `strict=` parameter
|
||||
|
|
||||
9 | zip("a", "b", *zip("c"))
|
||||
10 | zip(zip("a"), strict=False)
|
||||
11 | zip(zip("a", strict=True))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ B905
|
||||
9 |
|
||||
10 | zip(range(3), strict=True)
|
||||
12 |
|
||||
13 | # OK
|
||||
|
|
||||
|
||||
B905.py:24:1: B905 `zip()` without an explicit `strict=` parameter
|
||||
|
|
||||
24 | # Errors (limited iterators).
|
||||
25 | zip([1, 2, 3], repeat(1, 1))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B905
|
||||
26 | zip([1, 2, 3], repeat(1, times=4))
|
||||
|
|
||||
|
||||
B905.py:25:1: B905 `zip()` without an explicit `strict=` parameter
|
||||
|
|
||||
25 | # Errors (limited iterators).
|
||||
26 | zip([1, 2, 3], repeat(1, 1))
|
||||
27 | zip([1, 2, 3], repeat(1, times=4))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B905
|
||||
|
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue