summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Sassoulas <pierre.sassoulas@gmail.com>2020-09-05 22:23:02 +0200
committerPierre Sassoulas <pierre.sassoulas@gmail.com>2020-12-31 11:48:37 +0100
commit8b95fac727db80c5d59057c8c3099d3dafa42c32 (patch)
tree5aa9279f337f99b85d0e9135a30963c2624d9840
parentfba1fa626574688521d73628e023b6b9d55198b7 (diff)
downloadpylint-git-8b95fac727db80c5d59057c8c3099d3dafa42c32.tar.gz
Restrict the number of classes affected by len-as-condition
-rw-r--r--pylint/checkers/refactoring/len_checker.py13
-rw-r--r--tests/functional/l/len_checks.py45
-rw-r--r--tests/functional/l/len_checks.txt3
3 files changed, 58 insertions, 3 deletions
diff --git a/pylint/checkers/refactoring/len_checker.py b/pylint/checkers/refactoring/len_checker.py
index 07ccf4ebe..ae4a502d0 100644
--- a/pylint/checkers/refactoring/len_checker.py
+++ b/pylint/checkers/refactoring/len_checker.py
@@ -63,11 +63,20 @@ class LenChecker(checkers.BaseChecker):
parent = node.parent
while isinstance(parent, astroid.BoolOp):
parent = parent.parent
-
# we're finally out of any nested boolean operations so check if
# this len() call is part of a test condition
if utils.is_test_condition(node, parent):
- self.add_message("len-as-condition", node=node)
+ instance = next(node.args[0].infer())
+ mother_classes = self.base_classes_of_node(instance)
+ affected_by_pep8 = any(
+ t in mother_classes for t in ["str", "tuple", "range", "list"]
+ )
+ if affected_by_pep8 and not self.instance_has_bool(instance):
+ self.add_message("len-as-condition", node=node)
+
+ @staticmethod
+ def instance_has_bool(class_def: astroid.ClassDef) -> bool:
+ return any(hasattr(f, "name") and f.name == "__bool__" for f in class_def.body)
@utils.check_messages("len-as-condition")
def visit_unaryop(self, node):
diff --git a/tests/functional/l/len_checks.py b/tests/functional/l/len_checks.py
index 216a7e672..98c33424a 100644
--- a/tests/functional/l/len_checks.py
+++ b/tests/functional/l/len_checks.py
@@ -7,7 +7,7 @@ if len('TEST'): # [len-as-condition]
if not len('TEST'): # [len-as-condition]
pass
-z = False
+z = []
if z and len(['T', 'E', 'S', 'T']): # [len-as-condition]
pass
@@ -102,3 +102,46 @@ def github_issue_1331_v4(*args):
b = bool(len(z)) # [len-as-condition]
c = bool(len('TEST') or 42) # [len-as-condition]
+
+
+def github_issue_1879():
+
+ class ClassWithBool(list):
+ def __bool__(self):
+ return True
+
+ class ClassWithoutBool(list):
+ pass
+
+ class ChildClassWithBool(ClassWithBool):
+ pass
+
+ class ChildClassWithoutBool(ClassWithoutBool):
+ pass
+
+ assert len(ClassWithBool())
+ # We could expect to not have a len-as-condition for ChildClassWithBool,
+ # but I don't think the required analysis is worth it.
+ assert len(ChildClassWithBool()) # [len-as-condition]
+ assert len(ClassWithoutBool()) # [len-as-condition]
+ assert len(ChildClassWithoutBool()) # [len-as-condition]
+ # assert len(range(0)) != 0
+
+ # pylint: disable=import-outside-toplevel
+ import numpy
+ numpy_array = numpy.array([0])
+ if len(numpy_array) > 0:
+ print('numpy_array')
+ if len(numpy_array):
+ print('numpy_array')
+ if numpy_array:
+ print('b')
+
+ import pandas as pd
+ pandas_df = pd.DataFrame()
+ if len(pandas_df):
+ print("this works, but pylint tells me not to use len() without comparison")
+ if len(pandas_df) > 0:
+ print("this works and pylint likes it, but it's not the solution intended by PEP-8")
+ if pandas_df:
+ print("this does not work (truth value of dataframe is ambiguous)")
diff --git a/tests/functional/l/len_checks.txt b/tests/functional/l/len_checks.txt
index 23e3f583f..7fbf5e8da 100644
--- a/tests/functional/l/len_checks.txt
+++ b/tests/functional/l/len_checks.txt
@@ -13,3 +13,6 @@ len-as-condition:98:github_issue_1331_v3:Do not use `len(SEQUENCE)` without comp
len-as-condition:101:github_issue_1331_v4:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty
len-as-condition:103::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty
len-as-condition:104::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty
+len-as-condition:125:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty
+len-as-condition:126:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty
+len-as-condition:127:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty