pylint icon indicating copy to clipboard operation
pylint copied to clipboard

False positive with E1136 unsubscriptable-object

Open sam-s opened this issue 8 years ago • 42 comments

Steps to reproduce

  1. Create file z.py
a = None
while True:
    if a is None or a["1"] == 0:
        a = {"1": 1}
    else:
        break
print("Done")

  1. run pylint z.py

Current behavior

E:  3,20: Value 'a' is unsubscriptable (unsubscriptable-object)

Expected behavior

no errors, no warnings

pylint --version output

pylint 1.7.1, 
astroid 1.5.2
Python 2.7.13 (default, Dec 18 2016, 07:03:39) 
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)]

sam-s avatar May 24 '17 19:05 sam-s

This has hit us too. We are using a variable that is initialized to None, but then gets assigned to dict in a loop.

I think something like this will trigger it too:

points = [{"lat": 39, "lon": -92}, {"lat": 39.1, "lon": -92.1}]

furthest_west = None
for p in points:
    if furthest_west is None:
        furthest_west = p
    else:
        if p['lon'] < furthest_west['lon']:
            furthest_west = p

not sure how you would solve this, seems like the inferencer would have to go into the if/else clauses to see what might get assigned to the variable to figure out that it's Union(None, dict).

erjiang avatar Jul 07 '17 18:07 erjiang

Even checking isinstance(thing, list) immediately prior to using thing will trigger this.

hughes avatar Mar 14 '18 03:03 hughes

Yes @hughes that's a problem that's still crippling pylint even today (but hopefully with the new number of committers we have, we might tackle it at some point). I'm referring here to the fact that pylint does not quite grasp control flow, that is, if you use isinstance, pylint will happily infer all potential values that a variable could have, disregarding the branch hints.

PCManticore avatar Mar 16 '18 16:03 PCManticore

I had this problem today opening a config file.

ch3ck avatar Sep 11 '18 21:09 ch3ck

+1

kelvinau avatar Sep 15 '18 17:09 kelvinau

I have this false positive too. I reference a variable instanced as none in the main object class, and then change it in function from an inherited class. Code example below; incomplete for brevity.

class Response():
    def __init__(self):
        self.raw_response = None

    def filter_raw_response(self):
        if not self.raw_response:
            return None
        num_datapoints = len(self.raw_response['Datapoints'])   # Error shows here
        if num_datapoints == 0:
            return None

class SpecificResponse(Response):

    def craft_response(self)
        # Variable instanced here:
        self.raw_response = self.client.get_metric_statistics()

sdmunozsierra avatar Oct 12 '18 17:10 sdmunozsierra

+1

I got this too when I tried to iterate over candidates, a matrix like variable. It reports item as NoneType and raise E1136 error. But, actually, it is a list.

candidates = [item for item in val_dataset if item[-1] == i]
candidates = random.sample(candidates,100)
for item in candidates:
    img_path = os.path.join(data_dir,item[0])
    out_path = os.path.join(output_folder,str(i))
    os.system('cp %s %s'%(img_path,out_path))

jiarongqiu avatar Oct 22 '18 07:10 jiarongqiu

False positive even if the custom class implements the __getitem__ method.

lucmos avatar Nov 15 '18 20:11 lucmos

Will this ever be fixed then?

melyux avatar Dec 03 '18 23:12 melyux

@melikyuksel Unless someone proposes a patch, it's unlikely.

PCManticore avatar Dec 04 '18 07:12 PCManticore

+1

I got this too when I tried to iterate over candidates, a matrix like variable. It reports item as NoneType and raise E1136 error. But, actually, it is a list.

candidates = [item for item in val_dataset if item[-1] == i]
candidates = random.sample(candidates,100)
for item in candidates:
    img_path = os.path.join(data_dir,item[0])
    out_path = os.path.join(output_folder,str(i))
    os.system('cp %s %s'%(img_path,out_path))

I had the same issue when using random.sample

nilselde avatar May 21 '19 14:05 nilselde

Would this actually be a false positive? Seems to me it is following the intended behavior.

Used when there is a reference to an object that initialized as a non-subscriptable object

http://pylint-messages.wikidot.com/messages:e1136

sgornick avatar May 31 '19 11:05 sgornick

@sgornic The solution there is suggestion to used [] to initialize an empty list instead of using None to remove the error but doing that cause an unwanted/undesirable behavior in python. See here https://nikos7am.com/posts/mutable-default-arguments/ +1 I am experiencing the same issue while initializing a variable with None and rewriting this with some other values given that program runs without an error

ravinderkhatri avatar Jul 15 '19 07:07 ravinderkhatri

There is also issue #2063 that seems to describe the same error.

andy-maier avatar Apr 12 '20 04:04 andy-maier

I also get this with the built-in Exception.args and believe it is the same error:

class HTTPError(Exception):

    def __init__(self, status, reason):
        super(HTTPError, self).__init__(status, reason)

    @property
    def status(self):
        return self.args[0]  # pylint reports: E1136: Value 'self.args' is unsubscriptable

    @property
    def reason(self):
        return self.args[1]  # same

andy-maier avatar Apr 12 '20 04:04 andy-maier

Just make your list Python3 compatible: i.e.: my_list = [1, 2, 3, 4] for _my in list(my_list): pass

alexyarochkin avatar Apr 23 '20 21:04 alexyarochkin

I'm also seeing this with __getitem__ implemented

jakeleventhal avatar Jun 26 '20 20:06 jakeleventhal

I'm getting the same with a Pandas DataFrame:

endog = dta["colName"]  # [E1136 unsubscriptable-object] Value 'dta' is unsubscriptable
...
dta[["c1", "c2"]]     # [E1136 unsubscriptable-object] Value 'dta' is unsubscriptable

Output of python --version: Python 3.6.10 :: Anaconda, Inc.

olbapjose avatar Aug 22 '20 11:08 olbapjose

a similar thing also occurs with bidict

technillogue avatar Oct 25 '20 21:10 technillogue

a similar thing also occurs with bidict

Bidict author here. Just noticed this spurious error from pylint when using Python 3.9 and found my way to this issue.

(.venv39) jab@tsmbp ~/s/bidict (dev)> pre-commit run -a
...
pylint...................................................................Failed
- hook id: pylint
- exit code: 2

************* Module bidict._typing
bidict/_typing.py:17:17: E1136: Value '_t.Union' is unsubscriptable (unsubscriptable-object)
bidict/_typing.py:20:6: E1136: Value '_t.Union' is unsubscriptable (unsubscriptable-object)
bidict/_typing.py:32:6: E1136: Value '_t.Union' is unsubscriptable (unsubscriptable-object)
bidict/_typing.py:33:6: E1136: Value '_t.Union' is unsubscriptable (unsubscriptable-object)

In all these cases, _t.Union refers to typing.Union, which is in fact subscriptable. See for example this code (picking the first line flagged above, bidict/_typing.py:17, as an example).

This result is given by pylint 2.6.0 (as can be seen here), the latest stable release.

The spurious error does not occur when using the same version of pylint under Python 3.6, 3.7, or 3.8.

Hope this helps.

jab avatar Oct 28 '20 14:10 jab

@technillogue / @jab thanks for your report/investigation. The problem you mention is a bit different from the original one (i.e not due to a lack of control flow) but linked to python 3.9. It seems to be the same issue as #3882.

hippo91 avatar Oct 29 '20 13:10 hippo91

Happens when using hinting:

def my_func(rgb: tuple[int, int, int]):
   ....

same error: E1136: Value 'tuple' is unsubscriptable (unsubscriptable-object)

ricardoquesada avatar Jan 04 '21 20:01 ricardoquesada

same error: E1136: Value 'tuple' is unsubscriptable (unsubscriptable-object)

tuple is unsubscribable (at least in 3.7, maybe not 3.9):

>>> from typing import Tuple
>>> Tuple[int, int, int]
typing.Tuple[int, int, int]
>>> tuple[int, int, int]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'type' object is not subscriptable

kbakk avatar Jan 04 '21 21:01 kbakk

@kbakk good point.

I was just copy&pasting code from here: https://docs.python.org/3/library/typing.html

ricardoquesada avatar Jan 04 '21 22:01 ricardoquesada

@kbakk @ricardoquesada thanks for your report. In fact, starting with python3.9, tuple is indeed subscriptable. With master branch of pylint and astroid, when linting the following code:

def my_func(rgb: tuple[int]) -> None:
    rgb = None
    return

with python3.7:

pylint --disable=all --enable=unsubscriptable-object bug_pylint_1498.py 
************* Module bug_pylint_1498
bug_pylint_1498.py:1:17: E1136: Value 'tuple' is unsubscriptable (unsubscriptable-object)

---------------------------------------------------------------------
Your code has been rated at -6.67/10 (previous run: 10.00/10, -16.67)

and with python3.9:

pylint --disable=all --enable=unsubscriptable-object bug_pylint_1498.py 

---------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: -6.67/10, +16.67)

I'm not closing this issue, because original issue is not the same as the one we are dealing with here.

hippo91 avatar Jan 05 '21 18:01 hippo91

This issue also appears where the variable in question is a class attribute, as in the following minimal example:

class MyClass:
    my_cashed_value = None

    def get_message(self):
        result = MyClass.my_cashed_value
        if result is not None:
            return result["message"]
        else:
            message = "my cached message"
            MyClass.my_cashed_value = {"message": message}
            return message

(using pylint 2.6.0 and python 3.8.3)

BenLatham avatar Jan 12 '21 13:01 BenLatham

@BenLatham thank for your report. In your case, pylint is understanding that my_cashed_value maybe None because of its initialization. If you change my_cashed_value = None for my_cashed_value = dict() then the issue disappears.

hippo91 avatar Jan 23 '21 16:01 hippo91

Similar issue here using python3.8. In this example

"""
whatever
"""
from typing import Optional, Dict


def func(i) -> Optional[Dict]:
    """
    func
    """
    return {i: i} if i else None


x = func(1)
print(x[1])

x = func(0)
print(x[0])

I get

09:20 $ pylint test.py 
************* Module test
test.py:18:6: E1136: Value 'x' is unsubscriptable (unsubscriptable-object)

-------------------------------------------------------------------
Your code has been rated at 2.86/10 (previous run: -1.43/10, +4.29)

on the second call to func. pylint is quite smart, but in more complex examples it seems to give up and simply report a variable that might be None to be unsubscriptable. Is that the case?

andrea-cassioli-maersk avatar Jun 22 '21 07:06 andrea-cassioli-maersk

Yes, @andrea-cassioli-maersk astroid do not have real control flow (no restriction on the inference after isinstance(x, dict) for example) but can handle simple cases like the one you gave.

Pierre-Sassoulas avatar Jun 22 '21 14:06 Pierre-Sassoulas

I'm still not understanding why pylint can infer this case correctly:

def func(i) -> Optional[Tuple]:
    return (i, i) if i else None

print(func(1)[0])  # OK
print(func(0)[0])  # unsubscriptable-object

but not this:

foo: Optional[Tuple] = None
print(foo and foo[0])  # unsubscriptable-object  (??)

belm0 avatar Nov 11 '21 04:11 belm0