1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
from __future__ import annotations
import typing
from sqlalchemy import Float
from sqlalchemy import func
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.sql.expression import ColumnElement
class Base(DeclarativeBase):
pass
class Interval(Base):
__tablename__ = "interval"
id: Mapped[int] = mapped_column(primary_key=True)
start: Mapped[int]
end: Mapped[int]
def __init__(self, start: int, end: int):
self.start = start
self.end = end
@hybrid_property
def length(self) -> int:
return self.end - self.start
# old way - chain decorators + modifiers
@hybrid_property
def _inst_radius(self) -> float:
return abs(self.length) / 2
@_inst_radius.expression
def old_radius(cls) -> ColumnElement[float]:
f1 = func.abs(cls.length, type_=Float())
expr = f1 / 2
# while we are here, check some Float[] / div type stuff
if typing.TYPE_CHECKING:
# EXPECTED_RE_TYPE: sqlalchemy.*Function\[builtins.float\*?\]
reveal_type(f1)
# EXPECTED_RE_TYPE: sqlalchemy.*ColumnElement\[builtins.float\*?\]
reveal_type(expr)
return expr
# new way - use the original decorator with inplace
@hybrid_property
def new_radius(self) -> float:
return abs(self.length) / 2
@new_radius.inplace.expression
@classmethod
def _new_radius_expr(cls) -> ColumnElement[float]:
f1 = func.abs(cls.length, type_=Float())
expr = f1 / 2
# while we are here, check some Float[] / div type stuff
if typing.TYPE_CHECKING:
# EXPECTED_RE_TYPE: sqlalchemy.*Function\[builtins.float\*?\]
reveal_type(f1)
# EXPECTED_RE_TYPE: sqlalchemy.*ColumnElement\[builtins.float\*?\]
reveal_type(expr)
return expr
i1 = Interval(5, 10)
i2 = Interval(7, 12)
l1: int = i1.length
rdo: float = i2.old_radius
rdn: float = i2.new_radius
expr1 = Interval.length.in_([5, 10])
expr2o = Interval.old_radius
expr2n = Interval.new_radius
expr3o = Interval.old_radius.in_([0.5, 5.2])
expr3n = Interval.new_radius.in_([0.5, 5.2])
if typing.TYPE_CHECKING:
# EXPECTED_RE_TYPE: builtins.int\*?
reveal_type(i1.length)
# EXPECTED_RE_TYPE: builtins.float\*?
reveal_type(i2.old_radius)
# EXPECTED_RE_TYPE: builtins.float\*?
reveal_type(i2.new_radius)
# EXPECTED_RE_TYPE: sqlalchemy.*._HybridClassLevelAccessor\[builtins.int\*?\]
reveal_type(Interval.length)
# EXPECTED_RE_TYPE: sqlalchemy.*._HybridClassLevelAccessor\[builtins.float\*?\]
reveal_type(Interval.old_radius)
# EXPECTED_RE_TYPE: sqlalchemy.*._HybridClassLevelAccessor\[builtins.float\*?\]
reveal_type(Interval.new_radius)
# EXPECTED_RE_TYPE: sqlalchemy.*.BinaryExpression\[builtins.bool\*?\]
reveal_type(expr1)
# EXPECTED_RE_TYPE: sqlalchemy.*._HybridClassLevelAccessor\[builtins.float\*?\]
reveal_type(expr2o)
# EXPECTED_RE_TYPE: sqlalchemy.*._HybridClassLevelAccessor\[builtins.float\*?\]
reveal_type(expr2n)
# EXPECTED_RE_TYPE: sqlalchemy.*.BinaryExpression\[builtins.bool\*?\]
reveal_type(expr3o)
# EXPECTED_RE_TYPE: sqlalchemy.*.BinaryExpression\[builtins.bool\*?\]
reveal_type(expr3n)
# test #9268
class Foo(Base):
val: bool
def needs_update_getter(self) -> bool:
return self.val
...
def needs_update_setter(self, value: bool) -> None:
self.val = value
needs_update: hybrid_property[bool] = hybrid_property(
needs_update_getter,
needs_update_setter,
)
|