diff options
author | hippo91 <guillaume.peillex@gmail.com> | 2019-08-06 15:51:39 +0200 |
---|---|---|
committer | Claudiu Popa <pcmanticore@gmail.com> | 2019-08-06 15:51:39 +0200 |
commit | 5309e1d6dacbcbc30f2a4b077903e551dedc2385 (patch) | |
tree | 65ddbbecf02ee3b65f008e2baea520175ae62b70 | |
parent | 41c95225fe398dd3f62f48d777080dc8827b145c (diff) | |
download | pylint-git-5309e1d6dacbcbc30f2a4b077903e551dedc2385.tar.gz |
Don't emit protected-acces for single underscore prefixed attributes in special methods
This PR prevents the emission of protected-access message when a single
underscore prefixed attribute is accessed inside a special method.
Close #1802
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | doc/whatsnew/2.4.rst | 5 | ||||
-rw-r--r-- | pylint/checkers/classes.py | 17 | ||||
-rw-r--r-- | tests/functional/access_to_protected_members.py | 42 | ||||
-rw-r--r-- | tests/functional/access_to_protected_members.txt | 3 | ||||
-rw-r--r-- | tests/unittest_checker_classes.py | 2 |
6 files changed, 73 insertions, 1 deletions
@@ -7,6 +7,11 @@ What's New in Pylint 2.4.0? Release date: TBA +* Don't emit ``protected-access`` when a single underscore prefixed attribute + is used inside a special method + + Close #1802 + * Fix the "statement" values in the PyLinter's stats reports by module. * Added a new check, ``invalid-overridden-method`` diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst index a01635146..ceaf437bf 100644 --- a/doc/whatsnew/2.4.rst +++ b/doc/whatsnew/2.4.rst @@ -103,6 +103,11 @@ New checkers Other Changes ============= +* Don't emit ``protected-access`` when a single underscore prefixed attribute is used + inside a special method + + Close #1802 + * ``len-as-condition`` now only fires when a ``len(x)`` call is made without an explicit comparison. The message and description accompanying this checker has been changed diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 79724656a..1bd298b35 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -1351,8 +1351,25 @@ a metaclass class method.", if _is_attribute_property(name, klass): return + # A licit use of protected member is inside a special method + if not attrname.startswith( + "__" + ) and self._is_called_inside_special_method(node): + return + self.add_message("protected-access", node=node, args=attrname) + @staticmethod + def _is_called_inside_special_method(node: astroid.node_classes.NodeNG) -> bool: + """ + Returns true if the node is located inside a special (aka dunder) method + """ + try: + frame_name = node.frame().name + except AttributeError: + return False + return frame_name and frame_name in PYMETHODS + def _is_type_self_call(self, expr): return ( isinstance(expr, astroid.Call) diff --git a/tests/functional/access_to_protected_members.py b/tests/functional/access_to_protected_members.py index 1c97a3b50..a88dee82b 100644 --- a/tests/functional/access_to_protected_members.py +++ b/tests/functional/access_to_protected_members.py @@ -57,3 +57,45 @@ class Issue1031(object): if self._attr == 1: return type(INST)._protected # [protected-access] return None + + +class Issue1802(object): + """Test for GitHub issue 1802""" + def __init__(self, value): + self._foo = value + self.__private = 2 * value + + def __eq__(self, other): + """Test a correct access as the access to protected member is in a special method""" + if isinstance(other, self.__class__): + answer = self._foo == other._foo + return answer and self.__private == other.__private # [protected-access] + return False + + def not_in_special(self, other): + """ + Test an incorrect access as the access to protected member is not inside a special method + """ + if isinstance(other, self.__class__): + return self._foo == other._foo # [protected-access] + return False + + def __le__(self, other): + """ + Test a correct access as the access to protected member + is inside a special method even if it is deeply nested + """ + if 2 > 1: + if isinstance(other, self.__class__): + if "answer" == "42": + return self._foo == other._foo + return False + + def __fake_special__(self, other): + """ + Test an incorrect access as the access + to protected member is not inside a licit special method + """ + if isinstance(other, self.__class__): + return self._foo == other._foo # [protected-access] + return False diff --git a/tests/functional/access_to_protected_members.txt b/tests/functional/access_to_protected_members.txt index fb5c152e6..f1a9ceaa4 100644 --- a/tests/functional/access_to_protected_members.txt +++ b/tests/functional/access_to_protected_members.txt @@ -4,3 +4,6 @@ protected-access:42::Access to a protected member _protected of a client class protected-access:43::Access to a protected member _cls_protected of a client class protected-access:44::Access to a protected member _cls_protected of a client class protected-access:58:Issue1031.incorrect_access:Access to a protected member _protected of a client class +protected-access:72:Issue1802.__eq__:Access to a protected member __private of a client class +protected-access:80:Issue1802.not_in_special:Access to a protected member _foo of a client class +protected-access:100:Issue1802.__fake_special__:Access to a protected member _foo of a client class diff --git a/tests/unittest_checker_classes.py b/tests/unittest_checker_classes.py index 3925cc36d..da880362b 100644 --- a/tests/unittest_checker_classes.py +++ b/tests/unittest_checker_classes.py @@ -105,7 +105,7 @@ class TestVariablesChecker(CheckerTestCase): return 1 if self._nargs else 2 class Application(metaclass=MC): - def __new__(cls): + def __no_special__(cls): nargs = obj._nargs #@ """ ) |