diff options
author | Pierre Sassoulas <pierre.sassoulas@gmail.com> | 2020-09-05 22:23:02 +0200 |
---|---|---|
committer | Pierre Sassoulas <pierre.sassoulas@gmail.com> | 2020-12-31 11:48:37 +0100 |
commit | 8b95fac727db80c5d59057c8c3099d3dafa42c32 (patch) | |
tree | 5aa9279f337f99b85d0e9135a30963c2624d9840 | |
parent | fba1fa626574688521d73628e023b6b9d55198b7 (diff) | |
download | pylint-git-8b95fac727db80c5d59057c8c3099d3dafa42c32.tar.gz |
Restrict the number of classes affected by len-as-condition
-rw-r--r-- | pylint/checkers/refactoring/len_checker.py | 13 | ||||
-rw-r--r-- | tests/functional/l/len_checks.py | 45 | ||||
-rw-r--r-- | tests/functional/l/len_checks.txt | 3 |
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 |