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

False positive with E1136 unsubscriptable-object #1498

Open
sam-s opened this issue May 24, 2017 · 40 comments
Open

False positive with E1136 unsubscriptable-object #1498

sam-s opened this issue May 24, 2017 · 40 comments
Labels
Control flow Requires control flow understanding Enhancement ✨ Improvement to a component Hacktoberfest Help wanted 🙏 Outside help would be appreciated, good for new contributors High priority Issue with more than 10 reactions Needs PR This issue is accepted, sufficiently specified and now needs an implementation

Comments

@sam-s
Copy link

sam-s commented May 24, 2017

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)]
@rogalski rogalski added the Control flow Requires control flow understanding label May 31, 2017
@erjiang
Copy link

erjiang commented Jul 7, 2017

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).

@hughes
Copy link

hughes commented Mar 14, 2018

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

rjarry added a commit to rjarry/buildbot that referenced this issue Mar 15, 2018
This check is flaky and sometimes reports false-positive errors.

  pylint-dev/pylint#1498

Disable the check since we cannot rely on it to find report actual bugs.

Signed-off-by: Robin Jarry <robin@jarry.cc>
@PCManticore
Copy link
Contributor

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 PCManticore added the Enhancement ✨ Improvement to a component label Mar 16, 2018
@ch3ck
Copy link

ch3ck commented Sep 11, 2018

I had this problem today opening a config file.

@kelvinau
Copy link

+1

@sdmunozsierra
Copy link

sdmunozsierra commented Oct 12, 2018

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()

@jiarongqiu
Copy link

+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))

@lucmos
Copy link

lucmos commented Nov 15, 2018

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

@melyux
Copy link

melyux commented Dec 3, 2018

Will this ever be fixed then?

@PCManticore
Copy link
Contributor

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

@nilselde
Copy link

+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

@sgornick
Copy link

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

@ravinderkhatri
Copy link

ravinderkhatri commented Jul 15, 2019

@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

digitalfrost added a commit to digitalfrost/Uranium that referenced this issue Aug 19, 2019
The warning is because args in initialized as None
See pylint-dev/pylint#1498
False positive with unsubscriptable-object...
@andy-maier
Copy link

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

@andy-maier
Copy link

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

@alexyarochkin
Copy link

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

@jakeleventhal
Copy link

jakeleventhal commented Jun 26, 2020

I'm also seeing this with __getitem__ implemented

@olbapjose
Copy link

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.

@technillogue
Copy link

a similar thing also occurs with bidict

@hippo91
Copy link
Contributor

hippo91 commented Jan 5, 2021

@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.

@BenLatham
Copy link

BenLatham commented Jan 12, 2021

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)

@hippo91
Copy link
Contributor

hippo91 commented Jan 23, 2021

@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.

VladimirSlavik added a commit to VladimirSlavik/anaconda that referenced this issue Feb 23, 2021
Pylint does not see __members__ as existing or subscribable.

Maybe see also: pylint-dev/pylint#1498
VladimirSlavik added a commit to VladimirSlavik/anaconda that referenced this issue Mar 29, 2021
Pylint does not see __members__ as existing or subscribable.

Maybe see also: pylint-dev/pylint#1498

(cherry picked from commit 13f061d)
@Pierre-Sassoulas Pierre-Sassoulas added the High priority Issue with more than 10 reactions label May 30, 2021
@andrea-cassioli-maersk
Copy link

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?

@Pierre-Sassoulas
Copy link
Member

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 Pierre-Sassoulas added the Help wanted 🙏 Outside help would be appreciated, good for new contributors label Jul 1, 2021
@belm0
Copy link
Contributor

belm0 commented Nov 11, 2021

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  (??)

@DanielNoord
Copy link
Collaborator

I think we can infer that in the first call of the first example bool(i) evaluates to True and thus (i, i) will be returned. Similarly for the second line of that example.

For the second example we only infer that foo is None. We (currently) do nothing with the typing added to foo. Based on the inference of None the message is emitted.

@belm0
Copy link
Contributor

belm0 commented Nov 11, 2021

I see.

It would be a small quality of life improvement, but even without using the type annotation, pylint could certainly infer that foo can't be None (the initialized value), acknowledge that it doesn't know the type, and refrain from omitting the error.

@DanielNoord
Copy link
Collaborator

Perhaps I don't understand your examples but in both examples the unsubscriptable-object seems correct? In the first bool(0) will be False and thus func returns None whereas in the second example foo is initialised as None and isn't reassigned before the following line.

@belm0
Copy link
Contributor

belm0 commented Nov 11, 2021

the expression foo and foo[0] implies that foo can't possibly be indexed if it's None

but to clarify the example:

foo: Optional[Tuple] = None
# ... code that assigns a tuple to foo
print(foo and foo[0])  # unsubscriptable-object  (??)

it's actually a very common case, since variables declared type Optional[some_indexible_type] and initialized to None are very common

@DanielNoord
Copy link
Collaborator

Ah my bad, that's indeed quite obvious and a pattern we use ourselves. Should have examined the example better.

However, it is not quite that easy to add such control flow recognition to pylint. In fact, right now (I think) we do nothing of that type of control flow. Personally I have started to add some flow recognition for if ... elif, which is easier since we can check the parent node and check whether the current node is the child of an If.
The control flow needed for this example is something like checking whether we are in nodes.Comparison, whether the object being subscripted is in any of the preceding nodes of the Comparison, whether any of those nodes imply that the object is subscriptable, for example, if object is an int it would not be subscriptable but isinstance(foo, Tuple) would. Without any existing systems adding such control flow recognition is quite a daunting task and probably one of the reasons why this issue is still open and why we have so many open control flow issues.

@belm0
Copy link
Contributor

belm0 commented Nov 11, 2021

Another short-term idea: without fully supporting type hints, if pylint could at least see that the var is declared as a union including at least one indexible type, it should not emit unsubscriptable-object due to the high chance of false positive.

@dpinol

This comment was marked as spam.

@pstahlhofen
Copy link

+1
In my case, the object that is said to be unsubscriptable even though it actually is subscriptable is returned by a function from a library, so I cannot do any type-hinting or such to get rid of the wrong error message.

@matkoniecz
Copy link

following code also triggers it

maybe disable it by default as very likely to produce false positives?

    all_entries = []
    for key in ["surface", "surface:note"]:
        for entry in dubious_surfaces_requiring_notes.surface_values_that_trigger_note_creation(key):
            value = entry[0]
            comment = entry[1]
            all_entries.append((key, value, comment))
    for data in random.sample(all_entries, 6):
        print(data)
        key = data[0]
        value = data[1]
        comment = data[2]
        scan_for_tag(key, value, comment)

@optimaltayfun
Copy link

optimaltayfun commented Jan 12, 2024

I am getting this false positive as well. Below is the code triggering this:

from sqlalchemy.orm import Mapped, mapped_column
class UserDB(Base):
    """
    User database model.
    """
    __tablename__ = "users"
    user_id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True, autoincrement=True, nullable=False)

The documentation from sqlalchemy describes how to use Mapped in the following link:
https://docs.sqlalchemy.org/en/20/orm/mapping_styles.html

But this is causing false positives with pylint:
app/user_auth/database.py:49:13: E1136: Value 'Mapped' is unsubscriptable (unsubscriptable-object)

My pylint version:

pylint 3.0.3
astroid 3.0.2
Python 3.12.1 (main, Dec  8 2023, 05:40:51) [GCC 11.4.0]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Control flow Requires control flow understanding Enhancement ✨ Improvement to a component Hacktoberfest Help wanted 🙏 Outside help would be appreciated, good for new contributors High priority Issue with more than 10 reactions Needs PR This issue is accepted, sufficiently specified and now needs an implementation
Projects
None yet
Development

No branches or pull requests