sqlalchemy / sqlalchemy2-stubs

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

hybrid_property: Name "my_property" already defined #137

Open kasium opened 3 years ago

kasium commented 3 years ago

Describe the bug Using hybrid_property leads to several errors

Expected behavior No errors are returned

To Reproduce

from sqlalchemy import Column, Date, Time
from sqlalchemy.ext.hybrid import hybrid_property
from datetime import datetime

class DummyTable:
    create_date = Column("create_date", Date)
    create_time = Column("create_time", Time)

    @hybrid_property
    def create_timestamp(self) -> datetime:
        """create time property"""
        return datetime.combine(self.create_date, self.create_time)

    @create_timestamp.setter
    def create_timestamp(self, value:datetime) -> None:
        """create time setter"""
        self.create_date = value.date()
        self.create_time = value.time()

DummyTable().create_timestamp = datetime.now()

Error

src/x.py:14:6: error: Name "create_timestamp" already defined on line 9  [no-redef]
        @create_timestamp.setter
         ^
src/x.py:21:1: error: Cannot assign to a method  [assignment]
    DummyTable().create_timestamp = datetime.now()
    ^
src/x.py:21:33: error: Incompatible types in assignment (expression has type "datetime", variable has type "Callable[..., Any]")  [assignment]
    DummyTable().create_timestamp = datetime.now()

Versions.

CaselIT commented 3 years ago

Hi,

This is working as expected if you correctly map DummyTable. Sadly to get rid of Cannot assign to a method [assignment] you need to change name of the setter function.

from sqlalchemy import Column, Date, Time
from sqlalchemy.ext.hybrid import hybrid_property
from datetime import datetime
from sqlalchemy.orm import declarative_base
Base = declarative_base()

class DummyTable(Base):
    create_date = Column("create_date", Date)
    create_time = Column("create_time", Time)

    @hybrid_property
    def create_timestamp(self) -> datetime:
        """create time property"""
        return datetime.combine(self.create_date, self.create_time)

    @create_timestamp.setter
    def _(self, value: datetime) -> None:
        """create time setter"""
        self.create_date = value.date()
        self.create_time = value.time()

DummyTable().create_timestamp = datetime.now()
kasium commented 3 years ago

Thanks a lot!

kasium commented 3 years ago

So, I just tried to rename the functions, but this doesn't work. It seems like that the function names must be same resulting in mypy issues: https://docs.sqlalchemy.org/en/14/orm/extensions/hybrid.html#defining-expression-behavior-distinct-from-attribute-behavior

CaselIT commented 3 years ago

I've only checked if it fixed mypy error, I had not tried sqlalchemy.

I'm not sure if we can tell mypy to be happy about the name of the functions

kasium commented 3 years ago

Also, if I just ignore the duplicate names (# type:ignore[no-redef]), then mypy still complains about overloaded functions. I guess, mypy can't figure out which function to use in which case. Mabye the easiest way is, to allow SQLAlchemy to use any function name