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 - an overloaded method is reported to be unsubscriptable #5189

Closed
AChenQ opened this issue Oct 20, 2021 · 4 comments
Closed
Labels
Astroid Related to astroid Duplicate 🐫 Duplicate of an already existing issue False Positive 🦟 A message is emitted but nothing is wrong with the code

Comments

@AChenQ
Copy link

AChenQ commented Oct 20, 2021

Bug description

# pylint:disable=missing-module-docstring
# pylint:disable=missing-class-docstring
# pylint:disable=missing-function-docstring
# pylint:disable=too-few-public-methods

from typing import List, Sequence, Union, overload

_T = List[int]

class Dataset:
    data: List[_T] = [[i] for i in range(5)]

    @overload
    def __getitem__(self, index: int) -> _T:
        ...

    @overload
    def __getitem__(self, index: slice) -> Sequence[_T]:
        ...

    def __getitem__(self, index: Union[int, slice]) -> Union[_T, Sequence[_T]]:
        return self.data[index]


class_a = Dataset()
segment: _T = class_a[0]
data = segment[0] # pylint raise an unsubscriptable-object error here

Pylint output

************* Module a
a.py:27:7: E1136: Value 'segment' is unsubscriptable (unsubscriptable-object)

------------------------------------------------------------------
Your code has been rated at 6.15/10 (previous run: 8.89/10, -2.74)

Expected behavior

No pylint error report.

Pylint version

pylint 2.11.1
astroid 2.8.0
Python 3.7.9 (default, Aug 31 2020, 07:22:35)

OS / Environment

Mac OS

@AChenQ AChenQ added Bug 🪲 Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling labels Oct 20, 2021
AChenQ pushed a commit to AChenQ/tensorbay-python-sdk that referenced this issue Oct 25, 2021
AChenQ pushed a commit to AChenQ/tensorbay-python-sdk that referenced this issue Oct 25, 2021
related pylint issues:
unsubscriptable-object: pylint-dev/pylint#5189
no-value-for-parameter:  pylint-dev/pylint#2778
AChenQ pushed a commit to AChenQ/tensorbay-python-sdk that referenced this issue Oct 25, 2021
related pylint issues:
unsubscriptable-object: pylint-dev/pylint#5189
no-value-for-parameter:  pylint-dev/pylint#2778

PR Closed: Graviti-AI#1059
AChenQ pushed a commit to Graviti-AI/tensorbay-python-sdk that referenced this issue Oct 25, 2021
related pylint issues:
unsubscriptable-object: pylint-dev/pylint#5189
no-value-for-parameter:  pylint-dev/pylint#2778

PR Closed: #1059
@cdce8p cdce8p added C: unsubscriptable-object Issues related to 'unsubscriptable-object' check and removed C: unsubscriptable-object Issues related to 'unsubscriptable-object' check labels Nov 17, 2021
@DanielNoord DanielNoord added False Positive 🦟 A message is emitted but nothing is wrong with the code and removed Bug 🪲 Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling labels Feb 9, 2022
@finite-state-machine
Copy link

My thanks to @AChenQ for reporting this issue. I've tried to minimize the repro case just a little further, and try to give some idea of the root cause.

Consider the following code:

'''docstring
'''
from __future__ import annotations

from typing import (
        MutableMapping,
        overload,
        TypeVar,
        )


Tk = TypeVar('Tk')  # used for keys
Tv = TypeVar('Tv')  # used for values


# pylint: disable-next=too-few-public-methods
class ProxyBuilder:
    '''docstring
    '''
    # (in the following block, ignore[misc] disables "signatures
    # overload with incompatible return types":)
    @overload
    def wrap(  # type: ignore[misc]
            self, value: MutableMapping[Tk, Tv]
            ) -> MutableMapping[Tk, Tv]: ...
    @overload
    def wrap(self, value: Tv) -> Tv: ...

    def wrap(self, value: Tv) -> Tv:
        '''docstring

        Args:
            value: ...
        Returns:
            ...
        '''
        _ = self
        return value


def test_function() -> None:
    '''docstring
    '''
    proxy = ProxyBuilder().wrap({})
    proxy[1] = 'one'

What's (not) necessary for the issue to occur:

  • it's not important that the method in question is a dunder method
  • the issue still occurs even if all three return type annotations for wrap are concrete types which support subscripting, e.g., dict and tuple
  • the issue does not occur if the method in question is not within a class

Asteroid seems to be to blame

Asteroid appears to infer the return type of wrap is NoneType:
In pylint/checkers/typecheck.py, in the function visit_subscript(), near the following code (line 1869)...

        if supported_protocol and not supported_protocol(inferred, node):
            self.add_message(msg, args=node.value.as_string(), node=node.value)

...the value of repr(node) is e.g. <Const.NoneType l.None at 0x109946b50>.

Workarounds not ideal

This issue is particularly challenging to work around because it causes error to occur at every use of the return value, potentially raising dozens of errors for each call to wrap; it's impossible to suppress these with a single pylint: disable=... directive. (One could globally disable unsubscriptable-object, unsupported-assignment-operation, and unsupported-delete-operation, with the implied risk of false negatives.)

Python and pylint versions

Latest (pip) versions of pylint/asteroid are affected on Python 3.8 and 3.9.

pylint 2.12.2
astroid 2.9.3
Python 3.8.12 (default, Jan 17 2022, 13:29:00)
(or) Python 3.9.4 (default, Apr 25 2021, 12:02:02)
[Clang 10.0.1 (clang-1001.0.46.4)]

@lmgarret
Copy link

In case it helps, this simple example reproduces the issue too:

from typing import Union, List, overload


class SomeClass:
    @overload
    def some_method(self, a: int) -> int:
        ...

    @overload
    def some_method(self, a: str) -> List[int]:
        ...

    def some_method(self, a: Union[int, str]):
        if isinstance(a, int):
            return a
        elif isinstance(a, str):
            return [0, 0]
        else:
            raise ValueError(a)


SomeClass().some_method("bar")[0]
pylint 2.14.3
astroid 2.11.6
Python 3.8.10 (default, Mar 15 2022, 12:22:08) 
[GCC 9.4.0]

@jacobtylerwalls
Copy link
Member

Partner to pylint-dev/astroid#1015

@jacobtylerwalls jacobtylerwalls added the Astroid Related to astroid label Jun 25, 2022
@jacobtylerwalls
Copy link
Member

In fact, we are now closing @overload problems as duplicates of pylint-dev/astroid#1015, as they share a root cause.

@jacobtylerwalls jacobtylerwalls closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
@jacobtylerwalls jacobtylerwalls added the Duplicate 🐫 Duplicate of an already existing issue label Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Astroid Related to astroid Duplicate 🐫 Duplicate of an already existing issue False Positive 🦟 A message is emitted but nothing is wrong with the code
Projects
None yet
Development

No branches or pull requests

6 participants