Skip to content

Commit e4de78e

Browse files
committed
add b905: require strict= argument to zip()
1 parent 356f0dc commit e4de78e

File tree

4 files changed

+44
-3
lines changed

4 files changed

+44
-3
lines changed

README.rst

+4-1
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ or ``raise ... from None`` to distinguish them from errors in exception handling
200200
See `the exception chaining tutorial <https://docs.python.org/3/tutorial/errors.html#exception-chaining>`_
201201
for details.
202202

203+
**B905**: `zip()` without an explicit `strict=` parameter. Added with python3.10, so don't enable this flag for code that should work on previous versions. https://peps.python.org/pep-0618/
204+
203205
**B950**: Line too long. This is a pragmatic equivalent of
204206
``pycodestyle``'s ``E501``: it considers "max-line-length" but only triggers
205207
when the value has been exceeded by **more than 10%**. You will no
@@ -299,7 +301,8 @@ Change Log
299301

300302
Future
301303
~~~~~~~~~
302-
* B027: ignore @overload when typing is import with other names
304+
* B027: ignore @overload when typing is imported with other names
305+
* Add B905: `zip()` without an explicit `strict=` parameter.
303306

304307
22.10.27
305308
~~~~~~~~~

bugbear.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ def visit_Call(self, node):
356356

357357
self.check_for_b026(node)
358358

359+
self.check_for_b905(node)
359360
self.generic_visit(node)
360361

361362
def visit_Assign(self, node):
@@ -960,6 +961,14 @@ def check_for_b025(self, node):
960961
for duplicate in duplicates:
961962
self.errors.append(B025(node.lineno, node.col_offset, vars=(duplicate,)))
962963

964+
def check_for_b905(self, node):
965+
if (
966+
isinstance(node.func, ast.Name)
967+
and node.func.id == "zip"
968+
and not any(kw.arg == "strict" for kw in node.keywords)
969+
):
970+
self.errors.append(B905(node.lineno, node.col_offset))
971+
963972

964973
def compose_call_path(node):
965974
if isinstance(node, ast.Attribute):
@@ -1360,6 +1369,8 @@ def visit_Lambda(self, node):
13601369
)
13611370
)
13621371

1372+
B905 = Error(message="B905 `zip()` without an explicit `strict=` parameter.")
1373+
13631374
B950 = Error(message="B950 line too long ({} > {} characters)")
13641375

1365-
disabled_by_default = ["B901", "B902", "B903", "B904", "B950"]
1376+
disabled_by_default = ["B901", "B902", "B903", "B904", "B905", "B950"]

tests/b905_py310.py

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
zip()
2+
zip(range(3))
3+
zip("a", "b")
4+
zip("a", "b", *zip("c"))
5+
zip(zip("a"), strict=False)
6+
zip(zip("a", strict=True))
7+
8+
zip(range(3), strict=True)
9+
zip("a", "b", strict=False)
10+
zip("a", "b", "c", strict=True)

tests/test_bugbear.py

+18-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
B902,
4343
B903,
4444
B904,
45+
B905,
4546
B950,
4647
BugBearChecker,
4748
BugBearVisitor,
@@ -297,7 +298,7 @@ def test_b019(self):
297298
def test_b020(self):
298299
filename = Path(__file__).absolute().parent / "b020.py"
299300
bbc = BugBearChecker(filename=str(filename))
300-
errors = list(bbc.run())
301+
errors = list(e for e in bbc.run() if e[2][:4] == "B020")
301302
self.assertEqual(
302303
errors,
303304
self.errors(
@@ -484,6 +485,22 @@ def test_b904(self):
484485
]
485486
self.assertEqual(errors, self.errors(*expected))
486487

488+
@unittest.skipIf(sys.version_info < (3, 10), "requires 3.10+")
489+
def test_b905(self):
490+
filename = Path(__file__).absolute().parent / "b905_py310.py"
491+
bbc = BugBearChecker(filename=str(filename))
492+
errors = list(bbc.run())
493+
expected = [
494+
B905(1, 0),
495+
B905(2, 0),
496+
B905(3, 0),
497+
B905(4, 0),
498+
B905(4, 15),
499+
B905(5, 4),
500+
B905(6, 0),
501+
]
502+
self.assertEqual(errors, self.errors(*expected))
503+
487504
def test_b950(self):
488505
filename = Path(__file__).absolute().parent / "b950.py"
489506
bbc = BugBearChecker(filename=str(filename))

0 commit comments

Comments
 (0)