Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix invalid-overridden-method with nested property #4710

Merged
merged 1 commit into from
Jul 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ Release date: TBA

Closes #4698

* Fix ``invalid-overridden-method`` with nested property

Closes #4368


What's New in Pylint 2.9.3?
===========================
Expand Down
17 changes: 17 additions & 0 deletions pylint/checkers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@

import _string
import astroid
import astroid.objects

from pylint.constants import BUILTINS

Expand Down Expand Up @@ -794,6 +795,22 @@ def _is_property_decorator(decorator: astroid.Name) -> bool:
for ancestor in inferred.ancestors():
if ancestor.name == "property" and ancestor.root().name == BUILTINS:
return True
elif isinstance(inferred, astroid.FunctionDef):
# If decorator is function, check if it has exactly one return
# and the return is itself a function decorated with property
returns: List[astroid.Return] = list(
inferred._get_return_nodes_skip_functions()
)
if len(returns) == 1 and isinstance(
returns[0].value, (astroid.Name, astroid.Attribute)
):
inferred = safe_infer(returns[0].value)
if (
inferred
and isinstance(inferred, astroid.objects.Property)
and isinstance(inferred.function, astroid.FunctionDef)
):
return decorated_with_property(inferred.function)
return False


Expand Down
51 changes: 50 additions & 1 deletion tests/functional/i/invalid/invalid_overridden_method.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# pylint: disable=missing-docstring, too-few-public-methods
# pylint: disable=missing-docstring,too-few-public-methods,disallowed-name,invalid-name,unused-argument
import abc


Expand Down Expand Up @@ -76,3 +76,52 @@ class AbstractProperty:
@abc.abstractmethod
def prop(self):
return


# https://github.com/PyCQA/pylint/issues/4368
# Decrator functions with a nested property decorator should still be
# inferred as property.

def my_property(func):
@property
def _wrapper(self):
pass
return _wrapper

def not_a_property(func):
def _wrapper(self):
pass
return _wrapper

def multiple_returns(func):
def _wrapper(self):
pass
if foobar: # pylint: disable=undefined-variable
return False
return _wrapper

class A:
@property
def foo(self):
return True

@property
def bar(self):
return True

@property
def bar2(self):
return True

class B(A):
@my_property
def foo(self):
return False

@not_a_property
def bar(self): # [invalid-overridden-method]
return False

@multiple_returns
def bar2(self): # [invalid-overridden-method]
return False
2 changes: 2 additions & 0 deletions tests/functional/i/invalid/invalid_overridden_method.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ invalid-overridden-method:38:4:InvalidDerived.prop:Method 'prop' was expected to
invalid-overridden-method:41:4:InvalidDerived.async_method:Method 'async_method' was expected to be 'async', found it instead as 'non-async'
invalid-overridden-method:45:4:InvalidDerived.method_a:Method 'method_a' was expected to be 'method', found it instead as 'property'
invalid-overridden-method:48:4:InvalidDerived.method_b:Method 'method_b' was expected to be 'non-async', found it instead as 'async'
invalid-overridden-method:122:4:B.bar:Method 'bar' was expected to be 'property', found it instead as 'method'
invalid-overridden-method:126:4:B.bar2:Method 'bar2' was expected to be 'property', found it instead as 'method'