pylint
pylint copied to clipboard
false positive: unsupported-membership-test
Steps to reproduce
# pylint: disable=missing-docstring,too-few-public-methods
class MyClass:
_all = None
@classmethod
def all(cls):
if not cls._all:
cls._all = find_all()
return cls._all
@classmethod
def exist(cls, number):
return number in cls.all()
def find_all():
return [1, 2, 3]
if __name__ == '__main__':
assert MyClass.exist(2)
Current behavior
************* Module clsmemb
clsmemb.py:14:25: E1135: Value 'cls.all()' doesn't support membership test (unsupported-membership-test)
Expected behavior
--------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)
pylint --version output
pylint 2.3.1
astroid 2.2.5
Python 3.7.3 (default, Apr 24 2019, 15:29:51) [MSC v.1915 64 bit (AMD64)]
This looks like an issue that could be solved with better control flow inference, so that pylint could know that all could be either None or some other iterable value.
I'm not sure if it's the same issue or a separate one, but I'm also getting this false positive with subprocess.check_output on pylint 2.3.1 and astroid 2.2.5 (old Python version, though - 3.5.3 on Debian 9):
import subprocess
bytes_data = subprocess.check_output(['echo', 'hello']);
text_data = subprocess.check_output(['echo', 'hello'], universal_newlines=True);
print(b'bytes' in bytes_data) # Prints False
print('text' in text_data) # Prints False
'text' in bytes_data # Runtime TypeError
b'bytes' in text_data # Runtime TypeError
$ pylint -E check_pylint_e1135.py
************* Module check_pylint_e1135
check_pylint_e1135.py:6:18: E1135: Value 'bytes_data' doesn't support membership test (unsupported-membership-test)
check_pylint_e1135.py:7:16: E1135: Value 'text_data' doesn't support membership test (unsupported-membership-test)
check_pylint_e1135.py:8:10: E1135: Value 'bytes_data' doesn't support membership test (unsupported-membership-test)
check_pylint_e1135.py:9:12: E1135: Value 'text_data' doesn't support membership test (unsupported-membership-test)
Thanks for that report @ncoghlan It's probably a separate issue which we can resolve easier with a brain transform https://github.com/PyCQA/astroid/tree/master/astroid/brain so it's probably better to move the report to a separate issue.
Also bumped into this. Even simpler example to reproduce:
from typing import Optional, Tuple
prop = None # type: Optional[Tuple]
exists = prop and 'a' in prop
Out:
violation.py:5:27: E1135: Value 'prop' doesn't support membership test (unsupported-membership-test)
Equally adapting the code from the example above also generates a false positive for unsubscriptable-object in both Python 2, and Python 3 on latest version.
Python 2 & Pylint 1.6.5:
from typing import Optional
prop = None # type: Optional[dict]
if prop and 'a' in prop:
thing = prop['a']
Python 3 & Pylint 2.5.3:
from typing import Optional
prop: Optional[dict] = None
if prop and 'a' in prop:
thing = prop['a']
It might be worth to add that the pylint-issues reported from these minimal examples might be caused by the fixed assignment of None, in which case the type of prop can be inferred as NoneType and hence the warnings are correct.
from secrets import choice
from typing import Optional, Tuple
prop = choice([None, (None, )]) # type: Optional[Tuple]
exists = prop and 'a' in prop
does not produce a pylint issue.
Edit: For those landing here from a search engine: The issue from the initial example can be avoided by initializing _all = [] and avoiding None if there is no need to distinguish between the two values.
Another example, this time with a loop and TypedDict:
from typing import Optional, TypedDict
class ADict(TypedDict):
a: int
x: Optional[ADict] = None
while x is None or 'a' in x:
print('test')
# `x` gets assigned in the loop.
Also bumped into this. Even simpler example to reproduce:
from typing import Optional, Tuple prop = None # type: Optional[Tuple] exists = prop and 'a' in propOut:
violation.py:5:27: E1135: Value 'prop' doesn't support membership test (unsupported-membership-test)
I'm having this exact same problem for both unsupported-assignment-operation and unsupported-membership-test. Has there been any movement on getting this fixed? Looks to be a pretty old bug/FP
I believe my issue is the same. I'm running into it here:
I get the error from line 7 in this abbreviated code...
class MaintainedModel(Model):
class_label_filters: Optional[List[str]] = None
def __init__(self, *args, **kwargs):
self.label_filters = self.class_label_filters
def update_decorated_fields(self):
if self.label_filters is not None and "name" in self.label_filters:
...
Is there some way I should change my code to prevent the error? Funny thing is, I wasn't getting the error until I made an innocuous refactor of the code (to change global_label_filters to a class attribute (class_label_filters) with a type hint. There are other such variables without the error and the code works as intended...
Another recent example
from pydantic.v1.dataclasses import BaseModel, Field
class Foo(BaseModel):
my_list: list[str] = Field(default_factory=list)
The above started failing after a un-related refactor on pydantic v2 from my_list: list[str] = [] to use default_factory instead(to avoid a mutable default list)
'my_list' doesn't support membership test (unsupported-membership-test)
The type hints still specify it as a list.
Hi All! I ran into this (or something very similar) yesterday, when I tried to perform lazy initialization on some dicts. Here is a simplified example, I guess it is the same issue.
Example code:
# pylint: disable=C0114, C0115, R0903
####
# Case 1 (no class involved, works).
da: dict[str, str] | None = None # None at the beginning
da = {'a': 'Hello'} # Lazy initialization on demand
if 'a' in da: # OK
print(da['a']) # OK
da['a'] = "Hi" # OK
####
# Case 2 (class involved, error).
class MyClass:
db: dict[str, str] | None = None # None at the beginning
MyClass.db = {'b': 'World'} # Lazy initialization on demand
if 'b' in MyClass.db: # E1135
print(MyClass.db['b']) # E1136
MyClass.db['b'] = 'America' # E1137
####
pylint result:
example.py:20:10: E1135: Value 'MyClass.db' doesn't support membership test (unsupported-membership-test)
example.py:21:10: E1136: Value 'MyClass.db' is unsubscriptable (unsubscriptable-object)
example.py:22:0: E1137: 'MyClass.db' does not support item assignment (unsupported-assignment-operation)
pylint version (running on Ubuntu 22.04 LTS 64-bit):
pylint 3.2.7
astroid 3.2.4
Python 3.10.12 (main, Jul 29 2024, 16:56:48) [GCC 11.4.0]
The two main points of this example are probably:
- As @hepcat72 noted, class vs. no class does seem to make a difference in some (or all?) cases.
- The issue is not just about E1135, other error codes are also involved.
To answer your question @hepcat72 (I know it's fairly outdated, but may still be relevant to you or to others): The way I see it now, it is a pylint issue, I'd leave the code as is and wait for the pylint fix to arrive.
If this is the same issue (is it?), should we update the title? This is not just about unsupported membership test (E1135) then...
Hi!
Like in all the issues about the false positive cases for the not-an-iterable rule (ex. https://github.com/pylint-dev/pylint/issues/701), Pylint here tries again to check static typing of the objects, while it is not one of its main intended functionality, and so it does it badly in a lot of cases.
Here is my false positive case (do not judge the stupidity of this code, it just shows the problems in a simple case):
class A:
att: List[int] | None = None
@staticmethod
def f():
A.att = [1, 2, 3]
assert A.att is not None
for i in A.att: # ERROR: E1133:not-an-iterable
print(i)
if any(value in A.att for value in A.att): # 2 ERRORS: E1135:unsupported-membership-test on the first A.att, E1133:not-an-iterable on the second A.att
print("True")
The same way I said in the 701 issue: you should better delete this kind of rule. Pylint is not a static typing linter, and so it shouldn't do this kind of checks because it is too complex for its goal, particularly when the issues are still open several years after their first notification. If the Pylint developers still want to fix these rules about typing, so then Pylint must become a static typing linter.