Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

no implementation for hybrids available #171

Open
zzzeek opened this issue Sep 1, 2021 · 2 comments
Open

no implementation for hybrids available #171

zzzeek opened this issue Sep 1, 2021 · 2 comments
Labels
bug Something isn't working

Comments

@zzzeek
Copy link
Member

zzzeek commented Sep 1, 2021

re: #170

this worked for sqlalchemy-stubs, but does not seem to work here

POC from dropbox/sqlalchemy-stubs#98 (comment)

from typing import Any
from typing import Callable
from typing import Generic
from typing import Optional
from typing import overload
from typing import Type
from typing import TypeVar
from typing import Union

from sqlalchemy import column
from sqlalchemy import Integer
from sqlalchemy.sql import ColumnElement

_T = TypeVar("_T")


class hybrid_property(Generic[_T]):
    def __init__(
        self,
        fget: Callable[[Any], _T],
        expr: Callable[[Any], ColumnElement[_T]],
    ):
        self.fget = fget
        self.expr = expr

    @overload
    def __get__(
        self, instance: None, owner: Optional[Type[Any]]
    ) -> "ColumnElement[_T]":
        ...

    @overload
    def __get__(self, instance: object, owner: Optional[Type[Any]]) -> _T:
        ...

    def __get__(
        self, instance: Union[object, None], owner: Optional[Type[Any]] = None
    ) -> Any:
        if instance is None:
            return self.expr(owner)
        else:
            return self.fget(instance)

    def expression(
        self, expr: "Callable[[Any], ColumnElement[_T]]"
    ) -> "hybrid_property[_T]":
        return hybrid_property(self.fget, expr)


class MyClass:
    def my_thing_inst(self) -> int:
        return 5

    def my_thing_expr(cls) -> "ColumnElement[int]":
        return column("five", Integer)

    my_thing = hybrid_property(my_thing_inst, my_thing_expr)


mc = MyClass()

int_value: int = mc.my_thing
expr: ColumnElement[int] = MyClass.my_thing
@CaselIT
Copy link
Member

CaselIT commented Sep 1, 2021

sure :)

@CaselIT
Copy link
Member

CaselIT commented Sep 1, 2021

The normal definition does not work with mypy:

import sqlalchemy as sa
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import registry

R: registry = registry()


@R.mapped
class SomeModel:
    __tablename__ = "some_model"
    a = sa.Column(sa.Float, primary_key=True)

    @hybrid_property
    def hp(self) -> float:
        return abs(self.a) / 2 if self.a else 0

    @hp.setter  # type:ignore[no-redef]
    def hp(self, value: float) -> None:
        self.a = value * 2

    @hp.deleter  # type:ignore[no-redef]
    def hp(self):
        self.a = None

    @hp.expression  # type:ignore[no-redef]
    def hp(cls) -> sa.sql.ColumnElement[sa.Float]:
        return sa.func.abs(cls.a) / 2


obj = SomeModel()
obj.hp + 1

this will error with:
test\files\hybrid.py:31: error: Unsupported operand types for + ("Callable[..., Any]" and "int") [operator]

This looks like a mypy bug, since removing @hp.setter and the other @hp definition it does better, but it still fails to get the float:
test\files\hybrid.py:15: error: Unsupported operand types for / ("object" and "int") [operator]

I've opened a pr, but it does not work. Can you take a look?

@zzzeek zzzeek changed the title please implement my code for hybrids no implementation for hybrids available Sep 1, 2021
@zzzeek zzzeek added the bug Something isn't working label Sep 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants