Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

not-callable false positive for class #8138

Open
emontnemery opened this issue Jan 30, 2023 · 7 comments
Open

not-callable false positive for class #8138

emontnemery opened this issue Jan 30, 2023 · 7 comments
Labels
False Positive 🦟 A message is emitted but nothing is wrong with the code Help wanted 🙏 Outside help would be appreciated, good for new contributors High priority Issue with more than 10 reactions Lib specific 💅 This affect the code from a particular library Needs PR This issue is accepted, sufficiently specified and now needs an implementation

Comments

@emontnemery
Copy link

emontnemery commented Jan 30, 2023

Bug description

Pylint complains about func.myfunc in the snippet below not being callable.

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

from typing import TYPE_CHECKING, Any, Type, TypeVar

_T = TypeVar("_T", bound=Any)

class myfunc:
    def __init__(self, *args: Any) -> None:
        pass

class _FunctionGenerator:
    if TYPE_CHECKING:
        @property
        def myfunc(self) -> Type[myfunc]:
            ...

func = _FunctionGenerator()

func.myfunc(1, 2, 3)

A concrete use case is in sqlalchemy/sqlalchemy#9189

Configuration

No response

Command used

pylint test4b.py

Pylint output

************* Module test4b
test4b.py:19:0: E1102: func.myfunc is not callable (not-callable)

Expected behavior

Pylint should not complain about func.myfunc not being callable

Pylint version

pylint 2.16.0b1
astroid 2.13.3
Python 3.10.4 (main, Dec  5 2022, 21:19:56) [GCC 9.4.0]

OS / Environment

Ubuntu

Additional dependencies

No response

@emontnemery emontnemery added the Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling label Jan 30, 2023
@mbyrnepr2 mbyrnepr2 added False Positive 🦟 A message is emitted but nothing is wrong with the code Needs PR This issue is accepted, sufficiently specified and now needs an implementation and removed Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling labels Jan 30, 2023
@mbyrnepr2
Copy link
Member

mbyrnepr2 commented Jan 30, 2023

Thanks @emontnemery for the report. I can reproduce this using the example over in the sqlalchemy issue (copy/paste below). The example in the description doesn't run successfully as a Python script however.

from sqlalchemy import func, select, Integer
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.sql.selectable import Select

class Base(DeclarativeBase):
    pass


class MyTable(Base):
    __tablename__ = "host_entry"

    id = mapped_column(Integer, primary_key=True)


def state_attrs_exist(attr: int | None) -> Select:
    """Check if a state attributes id exists in the states table."""
    return select(func.min(MyTable.id)).where(MyTable.id == attr)

@mbyrnepr2 mbyrnepr2 added the Lib specific 💅 This affect the code from a particular library label Jan 30, 2023
@emontnemery
Copy link
Author

The example in the description doesn't run successfully as a Python script however.

Sorry, I did not know that was a requirement. Should I edit the example?

@mbyrnepr2
Copy link
Member

mbyrnepr2 commented Jan 31, 2023

The example in the description doesn't run successfully as a Python script however.

Sorry, I did not know that was a requirement. Should I edit the example?

No worries! Its handy for us if the example is working code so that we can focus on only the reported false positive. If you have time to edit it then please feel free but otherwise the other example should be sufficient I think.
I mentioned it originally just as a heads-up and to clarify to any future readers.

@cdcadman
Copy link

cdcadman commented Jun 7, 2023

Here is a working script. The last line produces an error message in pylint, but only if the sqlalchemy version is at least 2.0.
C:\temp\sa_func_count.py:19:31: E1102: sa.func.count is not callable (not-callable)

import sqlalchemy as sa

metadata = sa.MetaData()

user = sa.Table(
    "user",
    metadata,
    sa.Column("user_id", sa.Integer, primary_key=True),
    sa.Column("user_name", sa.String(16), nullable=False),
    sa.Column("email_address", sa.String(60)),
    sa.Column("nickname", sa.String(50), nullable=False),
)

db_uri = "sqlite:///:memory:"
engine = sa.create_engine(db_uri)
with engine.connect() as conn:
    user.create(bind=conn)
    assert (
        conn.execute(sa.select(sa.func.count()).select_from(user)).fetchall()[0][0] == 0
    )

marcospri added a commit to hypothesis/lms that referenced this issue Aug 11, 2023
marcospri added a commit to hypothesis/lms that referenced this issue Aug 14, 2023
marcospri added a commit to hypothesis/lms that referenced this issue Aug 14, 2023
marcospri added a commit to hypothesis/lms that referenced this issue Aug 17, 2023
@andreehultgren
Copy link

+1

@luciidlou
Copy link

bump

@Pierre-Sassoulas Pierre-Sassoulas added Help wanted 🙏 Outside help would be appreciated, good for new contributors High priority Issue with more than 10 reactions labels Jul 1, 2024
@cdcadman
Copy link

cdcadman commented Jul 5, 2024

In the if TYPE_CHECKING block, the myfunc property is set to None, which is not callable. So the underlying issue is that pylint is using the if TYPE_CHECKING block to infer the type, but it is not using the type hint provided there (see discussion of type hint vs. inference here: #4813). Type inference comes from the astroid package, and I don't see a way to make it ignore type checking blocks.

Here is a modification of the original script which runs in python, but outputs the not-callable false positive:

# pylint: disable=invalid-name, missing-class-docstring, missing-function-docstring, missing-module-docstring, too-few-public-methods
from __future__ import annotations

from typing import TYPE_CHECKING, Any, Type


class myfunc:
    def __init__(self, *args: Any) -> None:
        pass


class _FunctionGenerator:
    def __getattr__(self, name: str) -> _FunctionGenerator:
        return _FunctionGenerator()

    def __call__(self, *args: Any) -> None:
        pass

    if TYPE_CHECKING:

        @property
        def myfunc(self) -> Type[myfunc]: ...


func = _FunctionGenerator()

func.myfunc(1, 2, 3)

If you modify the property definition to return something that is callable, then pylint does not output not-callable:

# pylint: disable=invalid-name, missing-class-docstring, missing-function-docstring, missing-module-docstring, too-few-public-methods
from __future__ import annotations

from typing import TYPE_CHECKING, Any, Type


class myfunc:
    def __init__(self, *args: Any) -> None:
        pass


class _FunctionGenerator:
    def __getattr__(self, name: str) -> _FunctionGenerator:
        return _FunctionGenerator()

    def __call__(self, *args: Any) -> None:
        pass

    if TYPE_CHECKING:

        @property
        def myfunc(self) -> Type[myfunc]:
            return myfunc


func = _FunctionGenerator()

func.myfunc(1, 2, 3)

Removing the if TYPE_CHECKING block also makes the not-callable output go away.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
False Positive 🦟 A message is emitted but nothing is wrong with the code Help wanted 🙏 Outside help would be appreciated, good for new contributors High priority Issue with more than 10 reactions Lib specific 💅 This affect the code from a particular library Needs PR This issue is accepted, sufficiently specified and now needs an implementation
Projects
None yet
Development

No branches or pull requests

6 participants