Skip to content

Commit

Permalink
ignore if using infinite iterators in B905 (#4914)
Browse files Browse the repository at this point in the history
  • Loading branch information
kyoto7250 committed Jun 8, 2023
1 parent ac4a4da commit 01d3d4b
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 51 deletions.
15 changes: 15 additions & 0 deletions crates/ruff/resources/test/fixtures/flake8_bugbear/B905.py
@@ -1,10 +1,25 @@
from itertools import count, cycle, repeat

# Errors
zip()
zip(range(3))
zip("a", "b")
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))
4 changes: 3 additions & 1 deletion crates/ruff/src/checkers/ast/mod.rs
Expand Up @@ -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);
Expand Down
Expand Up @@ -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;

Expand All @@ -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 {
Expand All @@ -28,10 +31,50 @@ 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
.push(Diagnostic::new(ZipWithoutExplicitStrict, expr.range()));
}
}
}

/// 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
B905.py:4:1: B905 `zip()` without an explicit `strict=` parameter
|
1 | zip()
4 | # Errors
5 | zip()
| ^^^^^ B905
2 | zip(range(3))
3 | zip("a", "b")
6 | zip(range(3))
7 | zip("a", "b")
|

B905.py:2:1: B905 `zip()` without an explicit `strict=` parameter
B905.py:5:1: B905 `zip()` without an explicit `strict=` parameter
|
2 | zip()
3 | zip(range(3))
5 | # Errors
6 | zip()
7 | zip(range(3))
| ^^^^^^^^^^^^^ B905
4 | zip("a", "b")
5 | zip("a", "b", *zip("c"))
8 | zip("a", "b")
9 | 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:6:1: B905 `zip()` without an explicit `strict=` parameter
|
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: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))
|
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:4:16: 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: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: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))
|
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:6:1: B905 `zip()` without an explicit `strict=` parameter
B905.py:9: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))
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
|


0 comments on commit 01d3d4b

Please sign in to comment.