summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Byrne <31762852+mbyrnepr2@users.noreply.github.com>2023-03-03 14:31:40 +0100
committerGitHub <noreply@github.com>2023-03-03 14:31:40 +0100
commit7f24c414a3b4ec2af92e3eddaeb5a26207e4fe10 (patch)
tree6183a058db6525a181a1d737b9ab3435312a0dce
parent43cc43202d3f08ec9da29d57a4655cc22ad160fa (diff)
downloadpylint-git-7f24c414a3b4ec2af92e3eddaeb5a26207e4fe10.tar.gz
Accept values of the form ``<class name>.<attribute name>`` for the `exclude-protected` list (#8364)
Closes #7343 Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
-rw-r--r--doc/whatsnew/fragments/7343.feature3
-rw-r--r--pylint/checkers/classes/class_checker.py23
-rw-r--r--tests/functional/p/protected_access.py11
-rw-r--r--tests/functional/p/protected_access.rc2
-rw-r--r--tests/functional/p/protected_access.txt8
5 files changed, 36 insertions, 11 deletions
diff --git a/doc/whatsnew/fragments/7343.feature b/doc/whatsnew/fragments/7343.feature
new file mode 100644
index 000000000..2e9fa83c1
--- /dev/null
+++ b/doc/whatsnew/fragments/7343.feature
@@ -0,0 +1,3 @@
+Accept values of the form ``<class name>.<attribute name>`` for the ``exclude-protected`` list.
+
+Closes #7343
diff --git a/pylint/checkers/classes/class_checker.py b/pylint/checkers/classes/class_checker.py
index 16e7cbd14..f4fc2a42a 100644
--- a/pylint/checkers/classes/class_checker.py
+++ b/pylint/checkers/classes/class_checker.py
@@ -815,6 +815,7 @@ a metaclass class method.",
"_replace",
"_source",
"_make",
+ "os._exit",
),
"type": "csv",
"metavar": "<protected access exclusions>",
@@ -1782,21 +1783,30 @@ a metaclass class method.",
or attrname in self.linter.config.exclude_protected
):
return
- klass = node_frame_class(node)
-
- # In classes, check we are not getting a parent method
- # through the class object or through super
- callee = node.expr.as_string()
# Typing annotations in function definitions can include protected members
if utils.is_node_in_type_annotation_context(node):
return
- # We are not in a class, no remaining valid case
+ # Return if `attrname` is defined at the module-level or as a class attribute
+ # and is listed in `exclude-protected`.
+ inferred = safe_infer(node.expr)
+ if (
+ inferred
+ and isinstance(inferred, (nodes.ClassDef, nodes.Module))
+ and f"{inferred.name}.{attrname}" in self.linter.config.exclude_protected
+ ):
+ return
+
+ klass = node_frame_class(node)
if klass is None:
+ # We are not in a class, no remaining valid case
self.add_message("protected-access", node=node, args=attrname)
return
+ # In classes, check we are not getting a parent method
+ # through the class object or through super
+
# If the expression begins with a call to super, that's ok.
if (
isinstance(node.expr, nodes.Call)
@@ -1812,6 +1822,7 @@ a metaclass class method.",
# Check if we are inside the scope of a class or nested inner class
inside_klass = True
outer_klass = klass
+ callee = node.expr.as_string()
parents_callee = callee.split(".")
parents_callee.reverse()
for callee in parents_callee:
diff --git a/tests/functional/p/protected_access.py b/tests/functional/p/protected_access.py
index 209f571c4..6587e50a7 100644
--- a/tests/functional/p/protected_access.py
+++ b/tests/functional/p/protected_access.py
@@ -3,6 +3,8 @@
# pylint: disable=missing-function-docstring, invalid-metaclass, no-member
# pylint: disable=no-self-argument, undefined-variable, unused-variable
+import os
+
# Test that exclude-protected can be used to exclude names from protected-access warning
class Protected:
def __init__(self):
@@ -41,3 +43,12 @@ class Light:
def func(light: Light) -> None:
print(light._light_internal) # [protected-access]
+
+
+# os._exit is excluded from the protected-access check by default
+print(os._exit)
+
+# BaseTomato._sauce is included in the `exclude-protected` list
+# and does not emit a `protected-access` message:
+class BaseTomato:
+ _sauce = 42
diff --git a/tests/functional/p/protected_access.rc b/tests/functional/p/protected_access.rc
index 3ebab1b6f..c599f2a4a 100644
--- a/tests/functional/p/protected_access.rc
+++ b/tests/functional/p/protected_access.rc
@@ -1,2 +1,2 @@
[CLASSES]
-exclude-protected=_meta,_manager
+exclude-protected=_meta,_manager,os._exit,BaseTomato._sauce
diff --git a/tests/functional/p/protected_access.txt b/tests/functional/p/protected_access.txt
index 50e6c5d5b..e0bebb76a 100644
--- a/tests/functional/p/protected_access.txt
+++ b/tests/functional/p/protected_access.txt
@@ -1,4 +1,4 @@
-protected-access:17:0:17:9::Access to a protected member _teta of a client class:UNDEFINED
-protected-access:29:16:29:26:Application.__no_special__:Access to a protected member _nargs of a client class:UNDEFINED
-protected-access:39:14:39:35:Light.func:Access to a protected member _light_internal of a client class:UNDEFINED
-protected-access:43:10:43:31:func:Access to a protected member _light_internal of a client class:UNDEFINED
+protected-access:19:0:19:9::Access to a protected member _teta of a client class:UNDEFINED
+protected-access:31:16:31:26:Application.__no_special__:Access to a protected member _nargs of a client class:UNDEFINED
+protected-access:41:14:41:35:Light.func:Access to a protected member _light_internal of a client class:UNDEFINED
+protected-access:45:10:45:31:func:Access to a protected member _light_internal of a client class:UNDEFINED