@@ -56,8 +56,6 @@ Person(20, "Eve")
5656
5757## Signature of ` __init__ `
5858
59- TODO: All of the following tests are missing the ` self ` argument in the ` __init__ ` signature.
60-
6159Declarations in the class body are used to generate the signature of the ` __init__ ` method. If the
6260attributes are not just declarations, but also bindings, the type inferred from bindings is used as
6361the default value.
@@ -71,7 +69,7 @@ class D:
7169 y: str = " default"
7270 z: int | None = 1 + 2
7371
74- reveal_type(D.__init__ ) # revealed: (x: int, y: str = Literal["default"], z: int | None = Literal[3]) -> None
72+ reveal_type(D.__init__ ) # revealed: (self: D, x: int, y: str = Literal["default"], z: int | None = Literal[3]) -> None
7573```
7674
7775This also works if the declaration and binding are split:
@@ -82,7 +80,7 @@ class D:
8280 x: int | None
8381 x = None
8482
85- reveal_type(D.__init__ ) # revealed: (x: int | None = None) -> None
83+ reveal_type(D.__init__ ) # revealed: (self: D, x: int | None = None) -> None
8684```
8785
8886Non-fully static types are handled correctly:
@@ -96,7 +94,7 @@ class C:
9694 y: int | Any
9795 z: tuple[int , Any]
9896
99- reveal_type(C.__init__ ) # revealed: (x: Any, y: int | Any, z: tuple[int, Any]) -> None
97+ reveal_type(C.__init__ ) # revealed: (self: C, x: Any, y: int | Any, z: tuple[int, Any]) -> None
10098```
10199
102100Variables without annotations are ignored:
@@ -107,7 +105,7 @@ class D:
107105 x: int
108106 y = 1
109107
110- reveal_type(D.__init__ ) # revealed: (x: int) -> None
108+ reveal_type(D.__init__ ) # revealed: (self: D, x: int) -> None
111109```
112110
113111If attributes without default values are declared after attributes with default values, a
@@ -132,7 +130,7 @@ class D:
132130 y: ClassVar[str ] = " default"
133131 z: bool
134132
135- reveal_type(D.__init__ ) # revealed: (x: int, z: bool) -> None
133+ reveal_type(D.__init__ ) # revealed: (self: D, x: int, z: bool) -> None
136134
137135d = D(1 , True )
138136reveal_type(d.x) # revealed: int
@@ -150,7 +148,7 @@ class D:
150148 def y (self ) -> str :
151149 return " "
152150
153- reveal_type(D.__init__ ) # revealed: (x: int) -> None
151+ reveal_type(D.__init__ ) # revealed: (self: D, x: int) -> None
154152```
155153
156154And neither do nested class declarations:
@@ -163,7 +161,7 @@ class D:
163161 class Nested :
164162 y: str
165163
166- reveal_type(D.__init__ ) # revealed: (x: int) -> None
164+ reveal_type(D.__init__ ) # revealed: (self: D, x: int) -> None
167165```
168166
169167But if there is a variable annotation with a function or class literal type, the signature of
@@ -181,7 +179,7 @@ class D:
181179 class_literal: TypeOf[SomeClass]
182180 class_subtype_of: type[SomeClass]
183181
184- # revealed: (function_literal: def some_function() -> None, class_literal: <class 'SomeClass'>, class_subtype_of: type[SomeClass]) -> None
182+ # revealed: (self: D, function_literal: def some_function() -> None, class_literal: <class 'SomeClass'>, class_subtype_of: type[SomeClass]) -> None
185183reveal_type(D.__init__ )
186184```
187185
@@ -194,7 +192,7 @@ from typing import Callable
194192class D :
195193 c: Callable[[int ], str ]
196194
197- reveal_type(D.__init__ ) # revealed: (c: (int, /) -> str) -> None
195+ reveal_type(D.__init__ ) # revealed: (self: D, c: (int, /) -> str) -> None
198196```
199197
200198Implicit instance attributes do not affect the signature of ` __init__ ` :
@@ -209,7 +207,7 @@ class D:
209207
210208reveal_type(D(1 ).y) # revealed: str
211209
212- reveal_type(D.__init__ ) # revealed: (x: int) -> None
210+ reveal_type(D.__init__ ) # revealed: (self: D, x: int) -> None
213211```
214212
215213Annotating expressions does not lead to an entry in ` __annotations__ ` at runtime, and so it wouldn't
@@ -222,7 +220,7 @@ class D:
222220 (x): int = 1
223221
224222# TODO : should ideally not include a `x` parameter
225- reveal_type(D.__init__ ) # revealed: (x: int = Literal[1]) -> None
223+ reveal_type(D.__init__ ) # revealed: (self: D, x: int = Literal[1]) -> None
226224```
227225
228226## ` @dataclass ` calls with arguments
@@ -529,7 +527,7 @@ class C(Base):
529527 z: int = 10
530528 x: int = 15
531529
532- reveal_type(C.__init__ ) # revealed: (x: int = Literal[15], y: int = Literal[0], z: int = Literal[10]) -> None
530+ reveal_type(C.__init__ ) # revealed: (self: C, x: int = Literal[15], y: int = Literal[0], z: int = Literal[10]) -> None
533531```
534532
535533## Generic dataclasses
@@ -582,7 +580,7 @@ class UppercaseString:
582580class C :
583581 upper: UppercaseString = UppercaseString()
584582
585- reveal_type(C.__init__ ) # revealed: (upper: str = str) -> None
583+ reveal_type(C.__init__ ) # revealed: (self: C, upper: str = str) -> None
586584
587585c = C(" abc" )
588586reveal_type(c.upper) # revealed: str
@@ -628,7 +626,7 @@ class ConvertToLength:
628626class C :
629627 converter: ConvertToLength = ConvertToLength()
630628
631- reveal_type(C.__init__ ) # revealed: (converter: str = Literal[""]) -> None
629+ reveal_type(C.__init__ ) # revealed: (self: C, converter: str = Literal[""]) -> None
632630
633631c = C(" abc" )
634632reveal_type(c.converter) # revealed: int
@@ -667,7 +665,7 @@ class AcceptsStrAndInt:
667665class C :
668666 field: AcceptsStrAndInt = AcceptsStrAndInt()
669667
670- reveal_type(C.__init__ ) # revealed: (field: str | int = int) -> None
668+ reveal_type(C.__init__ ) # revealed: (self: C, field: str | int = int) -> None
671669```
672670
673671## ` dataclasses.field `
@@ -728,7 +726,7 @@ import dataclasses
728726class C :
729727 x: str
730728
731- reveal_type(C.__init__ ) # revealed: (x: str) -> None
729+ reveal_type(C.__init__ ) # revealed: (self: C, x: str) -> None
732730```
733731
734732### Dataclass with custom ` __init__ ` method
@@ -821,10 +819,57 @@ reveal_type(Person.__mro__) # revealed: tuple[<class 'Person'>, <class 'object'
821819The generated methods have the following signatures:
822820
823821``` py
824- # TODO : `self` is missing here
825- reveal_type(Person.__init__ ) # revealed: (name: str, age: int | None = None) -> None
822+ reveal_type(Person.__init__ ) # revealed: (self: Person, name: str, age: int | None = None) -> None
826823
827824reveal_type(Person.__repr__ ) # revealed: def __repr__(self) -> str
828825
829826reveal_type(Person.__eq__ ) # revealed: def __eq__(self, value: object, /) -> bool
830827```
828+
829+ ## Function-like behavior of synthesized methods
830+
831+ Here, we make sure that the synthesized methods of dataclasses behave like proper functions.
832+
833+ ``` toml
834+ [environment ]
835+ python-version = " 3.12"
836+ ```
837+
838+ ``` py
839+ from dataclasses import dataclass
840+ from typing import Callable
841+ from types import FunctionType
842+ from ty_extensions import CallableTypeOf, TypeOf, static_assert, is_subtype_of, is_assignable_to
843+
844+ @dataclass
845+ class C :
846+ x: int
847+
848+ reveal_type(C.__init__ ) # revealed: (self: C, x: int) -> None
849+ reveal_type(type (C.__init__ )) # revealed: <class 'FunctionType'>
850+
851+ # We can access attributes that are defined on functions:
852+ reveal_type(type (C.__init__ ).__code__ ) # revealed: CodeType
853+ reveal_type(C.__init__ .__code__ ) # revealed: CodeType
854+
855+ def equivalent_signature (self : C, x : int ) -> None :
856+ pass
857+
858+ type DunderInitType = TypeOf[C.__init__ ]
859+ type EquivalentPureCallableType = Callable[[C, int ], None ]
860+ type EquivalentFunctionLikeCallableType = CallableTypeOf[equivalent_signature]
861+
862+ static_assert(is_subtype_of(DunderInitType, EquivalentPureCallableType))
863+ static_assert(is_assignable_to(DunderInitType, EquivalentPureCallableType))
864+
865+ static_assert(not is_subtype_of(EquivalentPureCallableType, DunderInitType))
866+ static_assert(not is_assignable_to(EquivalentPureCallableType, DunderInitType))
867+
868+ static_assert(is_subtype_of(DunderInitType, EquivalentFunctionLikeCallableType))
869+ static_assert(is_assignable_to(DunderInitType, EquivalentFunctionLikeCallableType))
870+
871+ static_assert(not is_subtype_of(EquivalentFunctionLikeCallableType, DunderInitType))
872+ static_assert(not is_assignable_to(EquivalentFunctionLikeCallableType, DunderInitType))
873+
874+ static_assert(is_subtype_of(DunderInitType, FunctionType))
875+ ```
0 commit comments