pylint icon indicating copy to clipboard operation
pylint copied to clipboard

`not-callable` false positive for class

Open emontnemery opened this issue 1 year ago • 5 comments

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 https://github.com/sqlalchemy/sqlalchemy/issues/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 avatar Jan 30 '23 17:01 emontnemery

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 avatar Jan 30 '23 18:01 mbyrnepr2

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?

emontnemery avatar Jan 31 '23 07:01 emontnemery

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.

mbyrnepr2 avatar Jan 31 '23 08:01 mbyrnepr2

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
    )

cdcadman avatar Jun 07 '23 00:06 cdcadman

+1

andreehultgren avatar Feb 28 '24 08:02 andreehultgren