Before I begin, I just want to say Pylint is awesome. When I update from Python 3.6.5 to Python 3.8.2, it helped me figure out which built-in libraries had their API's changed (ex: time.clock's removal).
However, when I updated from 3.6.5 to 3.8.2, I discovered a Pylint bug as well. I would like to report a false positive on E1101, no-member.
I have found that when using a certain combination of:
typing.GenericpropertyThat pylint erroneously reports no-member.
In the below code, you can see:
base_property is a propertyBaseClass is a subclass of Genericbase_property is type hinted with a TypeVarBaseClass is subclassed twice --> GrandchildClass#!/usr/bin/env python3
"""Testing pylint raising of no-member."""
from typing import TypeVar, Generic
TFloatInt = TypeVar("TFloatInt", int, float)
class BaseClass(Generic[TFloatInt]):
@property
def base_property(self) -> TFloatInt:
return 1
class ChildClass(BaseClass[TFloatInt]):
"""A docstring."""
class GrandchildClass(BaseClass[int]):
def grandchild_method(self) -> int:
return self.base_property # E1101: Instance of 'GrandchildClass' has no 'base_property' member (no-member)
if __name__ == "__main__":
grandchild = GrandchildClass()
print(grandchild.grandchild_method())
Pylint output:
/path/to/my_module.py:27:15: E1101: Instance of 'GrandchildClass' has no 'base_property' member (no-member)
At runtime, this code works fine.
I believe the above code sample should not raise a no-member.
Note: this behavior depends on the Python version:
pylint 2.5.3
astroid 2.4.2
Python 3.8.2 (default, Jun 11 2020, 17:42:03)
[Clang 10.0.1 (clang-1001.0.46.4)]
Happens with the collections.abc objects too, as they are aliased by the typing module.
from typing import MutableMapping
class test(MutableMapping[str, int]):
def __init__(self):
self._data = {}
def __getitem__(self, key):
return self._data[key]
def __setitem__(self, key, value):
self._data[key] = value
def __delitem__(self, key):
del self._data[key]
def __iter__(self):
return iter(self._data)
def __len__(self):
return len(self._data)
a = test()
a.keys()
Results in:
test.py:26:0: E1101: Instance of 'test' has no 'keys' member (no-member)
This might be the same issue:
from typing import MutableMapping
d = {"a": 0}
if isinstance(d, MutableMapping):
print("It is!")
results in:
W1116: Second argument of isinstance is not a type (isinstance-second-argument-not-valid-type)
It seems like these generic types like MutableMapping are not being recognized correctly.
Could be related to https://github.com/PyCQA/pylint/issues/3131
Could be related to #3131
Yes, I think that is very likely. I tried to implement a solution in Astroid, but while I figured out why it didn't work, it wasn't clear to me how Astroid's internals are supposed to operate. If someone with a better understanding of Astroid's internals could answer these questions, I could continue with the PR.
For my repro above, I believe this issue no longer happens in pylint 2.7. Still happens for @xrogaan's repro though.