Skip to content

Commit

Permalink
Accept values of the form <class name>.<attribute name> for the `…
Browse files Browse the repository at this point in the history
…exclude-protected` list (#8364)


Closes #7343

Co-authored-by: Pierre Sassoulas <[email protected]>
  • Loading branch information
mbyrnepr2 and Pierre-Sassoulas authored Mar 3, 2023
1 parent 43cc432 commit 7f24c41
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 11 deletions.
3 changes: 3 additions & 0 deletions doc/whatsnew/fragments/7343.feature
Original file line number Diff line number Diff line change
@@ -0,0 1,3 @@
Accept values of the form ``<class name>.<attribute name>`` for the ``exclude-protected`` list.

Closes #7343
23 changes: 17 additions & 6 deletions pylint/checkers/classes/class_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 815,7 @@ class ClassChecker(BaseChecker):
"_replace",
"_source",
"_make",
"os._exit",
),
"type": "csv",
"metavar": "<protected access exclusions>",
Expand Down Expand Up @@ -1782,21 1783,30 @@ def _check_protected_attribute_access(
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)
Expand All @@ -1812,6 1822,7 @@ def _check_protected_attribute_access(
# 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:
Expand Down
11 changes: 11 additions & 0 deletions tests/functional/p/protected_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -41,3 43,12 @@ def func(light) -> None:

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
2 changes: 1 addition & 1 deletion tests/functional/p/protected_access.rc
Original file line number Diff line number Diff line change
@@ -1,2 1,2 @@
[CLASSES]
exclude-protected=_meta,_manager
exclude-protected=_meta,_manager,os._exit,BaseTomato._sauce
8 changes: 4 additions & 4 deletions tests/functional/p/protected_access.txt
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 7f24c41

Please sign in to comment.