pylint-dev / pylint

It's not just a linter that annoys you!
https://pylint.readthedocs.io/en/latest/
GNU General Public License v2.0
5.28k stars 1.13k forks source link

False positive unsubscriptable-object #9549

Open DarthLegiON opened 6 months ago

DarthLegiON commented 6 months ago

Bug description

Hi! I have weird behavior in my code.

Here is a code example:

from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql.sqltypes import String, Integer

class RoleReaction(Base):
    __tablename__ = 'user_roles_reactions'

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    role_name: Mapped[str] = mapped_column(String)
    reaction_name: Mapped[str] = mapped_column(String)
    group_id: Mapped[int] = mapped_column(ForeignKey('user_roles_groups.id'))
    group: Mapped[RoleGroup] = relationship(RoleGroup)

It causes several unsubscriptable-object errors in pylint (look into the output section).

But when I remove the last line (group definition), all of them disappear. When I move it upper, they don't. There is only one file in the whole project that causes such problem.

Configuration

[MASTER]
disable=
    C0114, # missing-module-docstring
    C0115, # missing-class-docstring
    C0116, # missing-function-docstring
    R0903, # too-few-public-methods
    R0801, # similar lines

extension-pkg-allow-list=
    dependency_injector,
    discord

max-line-length=120

Command used

PYTHONPATH=./src pylint --recursive=y --rcfile=.pylintrc ./src/roles_reactions/db.py

Pylint output

************* Module src.roles_reactions.db
src/roles_reactions/db.py:19:8: E1136: Value 'Mapped' is unsubscriptable (unsubscriptable-object)
src/roles_reactions/db.py:20:11: E1136: Value 'Mapped' is unsubscriptable (unsubscriptable-object)
src/roles_reactions/db.py:21:15: E1136: Value 'Mapped' is unsubscriptable (unsubscriptable-object)
src/roles_reactions/db.py:22:19: E1136: Value 'Mapped' is unsubscriptable (unsubscriptable-object)
src/roles_reactions/db.py:23:14: E1136: Value 'Mapped' is unsubscriptable (unsubscriptable-object)

------------------------------------------------------------------
Your code has been rated at 0.00/10 (previous run: 9.58/10, -9.58)

Expected behavior

No errors, 10/10

Pylint version

pylint 3.1.0
astroid 3.1.0
Python 3.10.5 (main, Aug  2 2022, 10:31:34) [GCC 10.2.1 20210110]

OS / Environment

Linux, docker

Additional dependencies

No response

DanielNoord commented 5 months ago

Hi, without the relevant import statements we won't be able to diagnose the issue. Please make sure to include all relevant code and dependencies :)

jenstroeger commented 5 months ago

@DarthLegiON thank you for the report, I’ve seen similar problems in my own project. Unfortunately, we didn’t investigate more but in our case deleting the virtual env and rebuilding it worked. Not a fix, I know, but it may unblock you?

@DanielNoord the code shown above is a SQLAlchemy v2 declarative table mapping (docs).

behnazh-w commented 5 months ago

We have faced a similar problem too.

@jenstroeger deleting the virtual env and rebuilding it doesn't fix our issue. We call pylint using a pre-commit hook. So, I ran pre-commit clean to clean up its cach, but still I face the same issue.

This is the error we get:

src/macaron/slsa_analyzer/checks/infer_artifact_pipeline_check.py:36:8: E1136: Value 'Mapped' is unsubscriptable (unsubscriptable-object)
src/macaron/slsa_analyzer/checks/infer_artifact_pipeline_check.py:39:16: E1136: Value 'Mapped' is unsubscriptable (unsubscriptable-object)
src/macaron/slsa_analyzer/checks/infer_artifact_pipeline_check.py:42:17: E1136: Value 'Mapped' is unsubscriptable (unsubscriptable-object)
src/macaron/slsa_analyzer/checks/infer_artifact_pipeline_check.py:45:13: E1136: Value 'Mapped' is unsubscriptable (unsubscriptable-object)

You can find the code here: https://github.com/oracle/macaron/blob/f9be5387ae5ed4e04ceabb9b86579929d2ddcb8f/src/macaron/slsa_analyzer/checks/infer_artifact_pipeline_check.py#L36-L45

Pierre-Sassoulas commented 5 months ago

Could you provide the output of pylint --version @behnazh-w, please ? @DarthLegiON I upgraded your example but we still miss the content of Base and RoleGroup, would you mind providing us the detail or create a minimal reproducer, please ?

behnazh-w commented 5 months ago

Could you provide the output of pylint --version @behnazh-w, please ?

pylint 3.1.0
astroid 3.1.0
Python 3.11.3 (main, Apr  4 2023, 22:36:41) [GCC 11.3.0]

sqlalchemy version: "SQLAlchemy >=2.0.0,<3.0.0", so 2.0.29 atm.

jenstroeger commented 5 months ago

I upgraded your example but we still miss the content of Base and RoleGroup, would you mind providing us the detail or create a minimal reproducer, please ?

Base is probably defined as described here and RoleGroup seems to be another table mapping just like the RoleReaction in the example. (It may even be enough to declare an __abstract__ table mapping, for the sake of a reproducible example?)

DarthLegiON commented 4 months ago

Sorry for the delay and thanks for your response!

Base definition:

Base = declarative_base()

Rolegroup definition:

class RoleGroup(Base):
    __tablename__ = 'user_roles_groups'

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    give_role_channel_id: Mapped[int] = mapped_column(BigInteger)
    give_role_message_id: Mapped[int] = mapped_column(BigInteger)

@jenstroeger about deleting a virtual env. Maybe it will surprise you but I don't use one even in development mode. I run the project in Docker. And it fails both in local environment and in CI pipeline, but docker and pip was rebuilt every time. Problem disappears only when I delete the last line with group definition.

leemurus commented 4 months ago

I have the same error for declared_attr decorated function. In pylint 2.* it was ok. sqlalchemy=2.0.30

class NetworkAwareModel(ApostroBaseModel):
    __abstract__ = True

    network_id: Mapped[int] = mapped_column(ForeignKey("network.id"))

    @declared_attr
    def network(self) -> Mapped[Network]:
        """Ensures that a new instance is created for each subclass"""
        return relationship("Network")
def network(self) -> Mapped[Network]:E1136: Value 'Mapped' is unsubscriptable (unsubscriptable-object)
DarthLegiON commented 4 months ago

If you're still intersted, here is my requirements.txt:

aiohttp==3.9.3
aiosignal==1.3.1
alembic==1.13.1
astroid==3.1.0
async-timeout==4.0.3
attrs==23.2.0
certifi==2024.2.2
dependency-injector==4.41.0
dill==0.3.8
discord==2.3.2
discord-py-slash-command==4.2.1
discord.py==2.3.2
frozenlist==1.4.1
greenlet==3.0.3
idna==3.6
isort==5.13.2
Mako==1.3.2
MarkupSafe==2.1.5
mccabe==0.7.0
multidict==6.0.5
platformdirs==4.2.0
psycopg2-binary==2.9.9
sentry-sdk==1.44.1
six==1.16.0
SQLAlchemy==2.0.29
tomli==2.0.1
tomlkit==0.12.4
typing_extensions==4.11.0
urllib3==2.2.1
yarl==1.9.4