summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>2021-10-31 05:55:46 +0100
committerGitHub <noreply@github.com>2021-10-31 05:55:46 +0100
commitdb5d77c2849e1ee20adef401d1b56c19e1c0fb55 (patch)
tree8500b065a94fe7ce92bea4fdcab1db5bc779742e
parent1d3a7ff32b0f6d819b17ce18345502fbc47b48c9 (diff)
downloadpylint-git-db5d77c2849e1ee20adef401d1b56c19e1c0fb55.tar.gz
Fix ``protected-access`` for attributes and methods of nested classes (#5232)
* Fix access to private function in inner class on protected-access bug * Add functional test for protected-access from inner class * Add Ikraduya to CONTRIBUTORS file * Add if statement to avoid potential bug * Fix ``protected-access`` for attributes and methods of nested classes This closes #3066 Co-authored-by: ikraduya <ikraduya@gmail.com> Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
-rw-r--r--CONTRIBUTORS.txt2
-rw-r--r--ChangeLog4
-rw-r--r--doc/whatsnew/2.12.rst4
-rw-r--r--pylint/checkers/classes.py16
-rw-r--r--pylint/checkers/utils.py7
-rw-r--r--tests/functional/a/access/access_to_protected_members.py64
-rw-r--r--tests/functional/a/access/access_to_protected_members.txt42
7 files changed, 123 insertions, 16 deletions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index b4bab0120..cc8393996 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -570,3 +570,5 @@ contributors:
* Takahide Nojima: contributor
* Tushar Sadhwani (tusharsadhwani): contributor
+
+* Ikraduya Edian: contributor
diff --git a/ChangeLog b/ChangeLog
index 8b1e97508..b35606e4b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -30,6 +30,10 @@ Release date: TBA
Closes #3197
+* Fixed ``protected-access`` for accessing of attributes and methods of inner classes
+
+ Closes #3066
+
* Added support for ``ModuleNotFoundError`` (``import-error`` and ``no-name-in-module``).
``ModuleNotFoundError`` inherits from ``ImportError`` and was added in Python ``3.6``
diff --git a/doc/whatsnew/2.12.rst b/doc/whatsnew/2.12.rst
index 627a5eb07..98008e109 100644
--- a/doc/whatsnew/2.12.rst
+++ b/doc/whatsnew/2.12.rst
@@ -78,6 +78,10 @@ Other Changes
``use-implicit-booleaness-not-len`` in order to be consistent with
``use-implicit-booleaness-not-comparison``.
+* Fixed ``protected-access`` for accessing of attributes and methods of inner classes
+
+ Closes #3066
+
* Update ``literal-comparison``` checker to ignore tuple literals
Closes #3031
diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py
index f6db8a73f..730f2e659 100644
--- a/pylint/checkers/classes.py
+++ b/pylint/checkers/classes.py
@@ -63,6 +63,7 @@ from pylint.checkers.utils import (
class_is_abstract,
decorated_with,
decorated_with_property,
+ get_outer_class,
has_known_bases,
is_attr_private,
is_attr_protected,
@@ -1606,9 +1607,22 @@ a metaclass class method.",
if self._is_type_self_call(node.expr):
return
+ # Check if we are inside the scope of a class or nested inner class
+ inside_klass = True
+ outer_klass = klass
+ parents_callee = callee.split(".")
+ parents_callee.reverse()
+ for callee in parents_callee:
+ if callee != outer_klass.name:
+ inside_klass = False
+ break
+
+ # Move up one level within the nested classes
+ outer_klass = get_outer_class(outer_klass)
+
# We are in a class, one remaining valid cases, Klass._attr inside
# Klass
- if not (callee == klass.name or callee in klass.basenames):
+ if not (inside_klass or callee in klass.basenames):
# Detect property assignments in the body of the class.
# This is acceptable:
#
diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py
index 3361eef8a..f5b4f42a5 100644
--- a/pylint/checkers/utils.py
+++ b/pylint/checkers/utils.py
@@ -673,6 +673,13 @@ def node_frame_class(node: nodes.NodeNG) -> Optional[nodes.ClassDef]:
return klass
+def get_outer_class(class_node: astroid.ClassDef) -> Optional[astroid.ClassDef]:
+ """Return the class that is the outer class of given (nested) class_node"""
+ parent_klass = class_node.parent.frame()
+
+ return parent_klass if isinstance(parent_klass, astroid.ClassDef) else None
+
+
def is_attr_private(attrname: str) -> Optional[Match[str]]:
"""Check that attribute name is private (at least two leading underscores,
at most one trailing underscore)
diff --git a/tests/functional/a/access/access_to_protected_members.py b/tests/functional/a/access/access_to_protected_members.py
index ffbfabf15..7d732dcc5 100644
--- a/tests/functional/a/access/access_to_protected_members.py
+++ b/tests/functional/a/access/access_to_protected_members.py
@@ -211,3 +211,67 @@ class Issue1159Subclass(Issue1159):
instance = Issue1159OtherClass()
instance._bar = 3 # [protected-access]
_ = instance._foo # [protected-access]
+
+
+class Issue3066:
+ """Test for GitHub issue 3066
+ Accessing of attributes/methods of inner and outer classes
+ https://github.com/PyCQA/pylint/issues/3066"""
+
+ attr = 0
+ _attr = 1
+
+ @staticmethod
+ def _bar(i):
+ """Docstring."""
+
+ @staticmethod
+ def foobar(i):
+ """Test access from outer class"""
+ Issue3066._attr = 2
+ Issue3066.Aclass._attr = "y" # [protected-access]
+ Issue3066.Aclass.Bclass._attr = "b" # [protected-access]
+
+ Issue3066._bar(i)
+ Issue3066.Aclass._bar(i) # [protected-access]
+ Issue3066.Aclass.Bclass._bar(i) # [protected-access]
+
+ class Aclass:
+ """Inner class for GitHub issue 3066"""
+
+ _attr = "x"
+
+ @staticmethod
+ def foobar(i):
+ """Test access from inner class"""
+ Issue3066._attr = 2 # [protected-access]
+ Issue3066.Aclass._attr = "y"
+ Issue3066.Aclass.Bclass._attr = "b" # [protected-access]
+
+ Issue3066._bar(i) # [protected-access]
+ Issue3066.Aclass._bar(i)
+ Issue3066.Aclass.Bclass._bar(i) # [protected-access]
+
+ @staticmethod
+ def _bar(i):
+ """Docstring."""
+
+ class Bclass:
+ """Inner inner class for GitHub issue 3066"""
+
+ _attr = "a"
+
+ @staticmethod
+ def foobar(i):
+ """Test access from inner inner class"""
+ Issue3066._attr = 2 # [protected-access]
+ Issue3066.Aclass._attr = "y" # [protected-access]
+ Issue3066.Aclass.Bclass._attr = "b"
+
+ Issue3066._bar(i) # [protected-access]
+ Issue3066.Aclass._bar(i) # [protected-access]
+ Issue3066.Aclass.Bclass._bar(i)
+
+ @staticmethod
+ def _bar(i):
+ """Docstring."""
diff --git a/tests/functional/a/access/access_to_protected_members.txt b/tests/functional/a/access/access_to_protected_members.txt
index 1b93683db..3f127f70c 100644
--- a/tests/functional/a/access/access_to_protected_members.txt
+++ b/tests/functional/a/access/access_to_protected_members.txt
@@ -1,16 +1,28 @@
-protected-access:19:14:MyClass.test:Access to a protected member _haha of a client class
-protected-access:41:0::Access to a protected member _protected of a client class
-protected-access:42:6::Access to a protected member _protected of a client class
-protected-access:43:0::Access to a protected member _cls_protected of a client class
-protected-access:44:6::Access to a protected member _cls_protected of a client class
-protected-access:58:19:Issue1031.incorrect_access:Access to a protected member _protected of a client class
-protected-access:72:48:Issue1802.__eq__:Access to a protected member __private of a client class
-protected-access:80:32:Issue1802.not_in_special:Access to a protected member _foo of a client class
-protected-access:100:32:Issue1802.__fake_special__:Access to a protected member _foo of a client class
-protected-access:162:8:Issue1159.access_other_attr:Access to a protected member _bar of a client class
-protected-access:163:12:Issue1159.access_other_attr:Access to a protected member _foo of a client class
+protected-access:19:14:MyClass.test:Access to a protected member _haha of a client class:HIGH
+protected-access:41:0::Access to a protected member _protected of a client class:HIGH
+protected-access:42:6::Access to a protected member _protected of a client class:HIGH
+protected-access:43:0::Access to a protected member _cls_protected of a client class:HIGH
+protected-access:44:6::Access to a protected member _cls_protected of a client class:HIGH
+protected-access:58:19:Issue1031.incorrect_access:Access to a protected member _protected of a client class:HIGH
+protected-access:72:48:Issue1802.__eq__:Access to a protected member __private of a client class:HIGH
+protected-access:80:32:Issue1802.not_in_special:Access to a protected member _foo of a client class:HIGH
+protected-access:100:32:Issue1802.__fake_special__:Access to a protected member _foo of a client class:HIGH
+protected-access:162:8:Issue1159.access_other_attr:Access to a protected member _bar of a client class:HIGH
+protected-access:163:12:Issue1159.access_other_attr:Access to a protected member _foo of a client class:HIGH
no-member:194:12:Issue1159Subclass.access_missing_member:Instance of 'Issue1159Subclass' has no '_baz' member; maybe '_bar'?:INFERENCE
-protected-access:194:12:Issue1159Subclass.access_missing_member:Access to a protected member _baz of a client class
-attribute-defined-outside-init:203:8:Issue1159Subclass.assign_missing_member:Attribute '_qux' defined outside __init__
-protected-access:212:8:Issue1159Subclass.access_other_attr:Access to a protected member _bar of a client class
-protected-access:213:12:Issue1159Subclass.access_other_attr:Access to a protected member _foo of a client class
+protected-access:194:12:Issue1159Subclass.access_missing_member:Access to a protected member _baz of a client class:HIGH
+attribute-defined-outside-init:203:8:Issue1159Subclass.assign_missing_member:Attribute '_qux' defined outside __init__:HIGH
+protected-access:212:8:Issue1159Subclass.access_other_attr:Access to a protected member _bar of a client class:HIGH
+protected-access:213:12:Issue1159Subclass.access_other_attr:Access to a protected member _foo of a client class:HIGH
+protected-access:232:8:Issue3066.foobar:Access to a protected member _attr of a client class:HIGH
+protected-access:233:8:Issue3066.foobar:Access to a protected member _attr of a client class:HIGH
+protected-access:236:8:Issue3066.foobar:Access to a protected member _bar of a client class:HIGH
+protected-access:237:8:Issue3066.foobar:Access to a protected member _bar of a client class:HIGH
+protected-access:247:12:Issue3066.Aclass.foobar:Access to a protected member _attr of a client class:HIGH
+protected-access:249:12:Issue3066.Aclass.foobar:Access to a protected member _attr of a client class:HIGH
+protected-access:251:12:Issue3066.Aclass.foobar:Access to a protected member _bar of a client class:HIGH
+protected-access:253:12:Issue3066.Aclass.foobar:Access to a protected member _bar of a client class:HIGH
+protected-access:267:16:Issue3066.Aclass.Bclass.foobar:Access to a protected member _attr of a client class:HIGH
+protected-access:268:16:Issue3066.Aclass.Bclass.foobar:Access to a protected member _attr of a client class:HIGH
+protected-access:271:16:Issue3066.Aclass.Bclass.foobar:Access to a protected member _bar of a client class:HIGH
+protected-access:272:16:Issue3066.Aclass.Bclass.foobar:Access to a protected member _bar of a client class:HIGH