sqlalchemy / sqlalchemy2-stubs

PEP-484 typing stubs for SQLAlchemy 1.4
MIT License
159 stars 41 forks source link

Type is wrongly inferenced #151

Open kasium opened 3 years ago

kasium commented 3 years ago

Describe the bug Not sure how to exactly explain it, but if I use a union, mapped and cast together, there is something wrong.

Expected behavior No mypy error

To Reproduce

from typing import Optional, Type, cast, Union
from sqlalchemy.orm import declarative_base, Mapped
from sqlalchemy import Column, Unicode

Base = declarative_base()

class DummyTable(Base):
    __tablename__ = "user"
    col1:Mapped[Optional[str]] = Column(Unicode, primary_key=True)

class DummyTable2(Base):
    __tablename__ = "user2"
    col1:Mapped[Optional[str]] = Column(Unicode, primary_key=True)

def x(arg: Type[Base]) -> None:
    arg = cast(Type[Union[DummyTable, DummyTable2]], arg)
    arg.col1.int_(1,2)

Error

x.py:17:5: error: Item "str" of "Optional[str]" has no attribute "int_"  [union-attr]
        arg.col1.int_(1,2)
        ^
x.py:17:5: error: Item "None" of "Optional[str]" has no attribute "int_"  [union-attr]
        arg.col1.int_(1,2)
        ^

Versions. OS: Linux Python: 3.7.1 SQLAlchemy: 1.4.22 mypy: 0.910 SQLAlchemy2-stubs: 0.0.2a6

Have a nice day!

provinzkraut commented 3 years ago

I've come across the same issue, and it seems to be related to Union. It is also reproducible by using a Union with the type itself:

from typing import Type, Union

from sqlalchemy import Column, String
from sqlalchemy.orm import declarative_base, Mapped

Base = declarative_base()

class Foo(Base):
    id = Column(String, primary_key=True)
    data: Mapped[str] = Column(String, nullable=False)

class Bar(Base):
    id = Column(String, primary_key=True)
    data: Mapped[str] = Column(String, nullable=False)

def do_something(mapped: Type[Union[Foo, Foo]]) -> None:
    mapped.data.in_(["a", "b"])

Result is:

 error: "str" has no attribute "in_"

Using any of the mapped classes outside of Union works fine.

tad3j commented 3 years ago

I think I'm noticing similar issue when trying to get column name via .key....since Column is typed as string, .key can't be called on it resulting

class UserModel(Base):
    __tablename__ = "users"
    username = Column(String, unique=True, index=True)
self.get_user_by_username = load_by_foreign_key(
    UserModel, UserModel.username.key
)

Pycharm is complaining: Unresolved attribute reference 'key' for class 'str'