@@ -818,40 +818,74 @@ def _(flag: bool):
818
818
if flag:
819
819
class C1 :
820
820
x = 1
821
+ y: int = 1
821
822
822
823
else :
823
824
class C1 :
824
825
x = 2
826
+ y: int | str = " b"
825
827
826
828
reveal_type(C1.x) # revealed: Unknown | Literal[1, 2]
829
+ reveal_type(C1.y) # revealed: int | str
830
+
831
+ C1.y = 100
832
+ # error: [invalid-assignment] "Object of type `Literal["problematic"]` is not assignable to attribute `y` on type `Literal[C1, C1]`"
833
+ C1.y = " problematic"
827
834
828
835
class C2 :
829
836
if flag:
830
837
x = 3
838
+ y: int = 3
831
839
else :
832
840
x = 4
841
+ y: int | str = " d"
833
842
834
843
reveal_type(C2.x) # revealed: Unknown | Literal[3, 4]
844
+ reveal_type(C2.y) # revealed: int | str
845
+
846
+ C2.y = 100
847
+ # error: [invalid-assignment] "Object of type `None` is not assignable to attribute `y` of type `int | str`"
848
+ C2.y = None
849
+ # TODO : should be an error, needs more sophisticated union handling in `validate_attribute_assignment`
850
+ C2.y = " problematic"
835
851
836
852
if flag:
837
853
class Meta3 (type ):
838
854
x = 5
855
+ y: int = 5
839
856
840
857
else :
841
858
class Meta3 (type ):
842
859
x = 6
860
+ y: int | str = " f"
843
861
844
862
class C3 (metaclass = Meta3 ): ...
845
863
reveal_type(C3.x) # revealed: Unknown | Literal[5, 6]
864
+ reveal_type(C3.y) # revealed: int | str
865
+
866
+ C3.y = 100
867
+ # error: [invalid-assignment] "Object of type `None` is not assignable to attribute `y` of type `int | str`"
868
+ C3.y = None
869
+ # TODO : should be an error, needs more sophisticated union handling in `validate_attribute_assignment`
870
+ C3.y = " problematic"
846
871
847
872
class Meta4 (type ):
848
873
if flag:
849
874
x = 7
875
+ y: int = 7
850
876
else :
851
877
x = 8
878
+ y: int | str = " h"
852
879
853
880
class C4 (metaclass = Meta4 ): ...
854
881
reveal_type(C4.x) # revealed: Unknown | Literal[7, 8]
882
+ reveal_type(C4.y) # revealed: int | str
883
+
884
+ C4.y = 100
885
+ # error: [invalid-assignment] "Object of type `None` is not assignable to attribute `y` of type `int | str`"
886
+ C4.y = None
887
+ # TODO : should be an error, needs more sophisticated union handling in `validate_attribute_assignment`
888
+ C4.y = " problematic"
855
889
```
856
890
857
891
## Unions with possibly unbound paths
@@ -875,8 +909,14 @@ def _(flag1: bool, flag2: bool):
875
909
# error: [possibly-unbound-attribute] "Attribute `x` on type `Literal[C1, C2, C3]` is possibly unbound"
876
910
reveal_type(C.x) # revealed: Unknown | Literal[1, 3]
877
911
912
+ # error: [invalid-assignment] "Object of type `Literal[100]` is not assignable to attribute `x` on type `Literal[C1, C2, C3]`"
913
+ C.x = 100
914
+
878
915
# error: [possibly-unbound-attribute] "Attribute `x` on type `C1 | C2 | C3` is possibly unbound"
879
916
reveal_type(C().x) # revealed: Unknown | Literal[1, 3]
917
+
918
+ # error: [invalid-assignment] "Object of type `Literal[100]` is not assignable to attribute `x` on type `C1 | C2 | C3`"
919
+ C().x = 100
880
920
```
881
921
882
922
### Possibly-unbound within a class
@@ -901,10 +941,16 @@ def _(flag: bool, flag1: bool, flag2: bool):
901
941
# error: [possibly-unbound-attribute] "Attribute `x` on type `Literal[C1, C2, C3]` is possibly unbound"
902
942
reveal_type(C.x) # revealed: Unknown | Literal[1, 2, 3]
903
943
944
+ # error: [possibly-unbound-attribute]
945
+ C.x = 100
946
+
904
947
# Note: we might want to consider ignoring possibly-unbound diagnostics for instance attributes eventually,
905
948
# see the "Possibly unbound/undeclared instance attribute" section below.
906
949
# error: [possibly-unbound-attribute] "Attribute `x` on type `C1 | C2 | C3` is possibly unbound"
907
950
reveal_type(C().x) # revealed: Unknown | Literal[1, 2, 3]
951
+
952
+ # error: [possibly-unbound-attribute]
953
+ C().x = 100
908
954
```
909
955
910
956
### Possibly-unbound within gradual types
@@ -922,6 +968,9 @@ def _(flag: bool):
922
968
x: int
923
969
924
970
reveal_type(Derived().x) # revealed: int | Any
971
+
972
+ Derived().x = 1
973
+ Derived().x = " a"
925
974
```
926
975
927
976
### Attribute possibly unbound on a subclass but not on a superclass
@@ -936,8 +985,10 @@ def _(flag: bool):
936
985
x = 2
937
986
938
987
reveal_type(Bar.x) # revealed: Unknown | Literal[2, 1]
988
+ Bar.x = 3
939
989
940
990
reveal_type(Bar().x) # revealed: Unknown | Literal[2, 1]
991
+ Bar().x = 3
941
992
```
942
993
943
994
### Attribute possibly unbound on a subclass and on a superclass
@@ -955,8 +1006,14 @@ def _(flag: bool):
955
1006
# error: [possibly-unbound-attribute]
956
1007
reveal_type(Bar.x) # revealed: Unknown | Literal[2, 1]
957
1008
1009
+ # error: [possibly-unbound-attribute]
1010
+ Bar.x = 3
1011
+
958
1012
# error: [possibly-unbound-attribute]
959
1013
reveal_type(Bar().x) # revealed: Unknown | Literal[2, 1]
1014
+
1015
+ # error: [possibly-unbound-attribute]
1016
+ Bar().x = 3
960
1017
```
961
1018
962
1019
### Possibly unbound/undeclared instance attribute
@@ -975,6 +1032,9 @@ def _(flag: bool):
975
1032
976
1033
# error: [possibly-unbound-attribute]
977
1034
reveal_type(Foo().x) # revealed: int | Unknown
1035
+
1036
+ # error: [possibly-unbound-attribute]
1037
+ Foo().x = 1
978
1038
```
979
1039
980
1040
#### Possibly unbound
@@ -989,6 +1049,9 @@ def _(flag: bool):
989
1049
# Emitting a diagnostic in a case like this is not something we support, and it's unclear
990
1050
# if we ever will (or want to)
991
1051
reveal_type(Foo().x) # revealed: Unknown | Literal[1]
1052
+
1053
+ # Same here
1054
+ Foo().x = 2
992
1055
```
993
1056
994
1057
### Unions with all paths unbound
@@ -1003,6 +1066,11 @@ def _(flag: bool):
1003
1066
1004
1067
# error: [unresolved-attribute] "Type `Literal[C1, C2]` has no attribute `x`"
1005
1068
reveal_type(C.x) # revealed: Unknown
1069
+
1070
+ # TODO : This should ideally be a `unresolved-attribute` error. We need better union
1071
+ # handling in `validate_attribute_assignment` for this.
1072
+ # error: [invalid-assignment] "Object of type `Literal[1]` is not assignable to attribute `x` on type `Literal[C1, C2]`"
1073
+ C.x = 1
1006
1074
```
1007
1075
1008
1076
## Inherited class attributes
@@ -1017,6 +1085,8 @@ class B(A): ...
1017
1085
class C (B ): ...
1018
1086
1019
1087
reveal_type(C.X) # revealed: Unknown | Literal["foo"]
1088
+
1089
+ C.X = " bar"
1020
1090
```
1021
1091
1022
1092
### Multiple inheritance
@@ -1040,6 +1110,8 @@ reveal_type(A.__mro__)
1040
1110
1041
1111
# `E` is earlier in the MRO than `F`, so we should use the type of `E.X`
1042
1112
reveal_type(A.X) # revealed: Unknown | Literal[42]
1113
+
1114
+ A.X = 100
1043
1115
```
1044
1116
1045
1117
## Intersections of attributes
@@ -1057,9 +1129,13 @@ class B: ...
1057
1129
def _ (a_and_b : Intersection[A, B]):
1058
1130
reveal_type(a_and_b.x) # revealed: int
1059
1131
1132
+ a_and_b.x = 2
1133
+
1060
1134
# Same for class objects
1061
1135
def _ (a_and_b : Intersection[type[A], type[B]]):
1062
1136
reveal_type(a_and_b.x) # revealed: int
1137
+
1138
+ a_and_b.x = 2
1063
1139
```
1064
1140
1065
1141
### Attribute available on both elements
@@ -1069,6 +1145,7 @@ from knot_extensions import Intersection
1069
1145
1070
1146
class P : ...
1071
1147
class Q : ...
1148
+ class R (P , Q ): ...
1072
1149
1073
1150
class A :
1074
1151
x: P = P()
@@ -1078,10 +1155,12 @@ class B:
1078
1155
1079
1156
def _ (a_and_b : Intersection[A, B]):
1080
1157
reveal_type(a_and_b.x) # revealed: P & Q
1158
+ a_and_b.x = R()
1081
1159
1082
1160
# Same for class objects
1083
1161
def _ (a_and_b : Intersection[type[A], type[B]]):
1084
1162
reveal_type(a_and_b.x) # revealed: P & Q
1163
+ a_and_b.x = R()
1085
1164
```
1086
1165
1087
1166
### Possible unboundness
@@ -1091,6 +1170,7 @@ from knot_extensions import Intersection
1091
1170
1092
1171
class P : ...
1093
1172
class Q : ...
1173
+ class R (P , Q ): ...
1094
1174
1095
1175
def _ (flag : bool ):
1096
1176
class A1 :
@@ -1102,11 +1182,17 @@ def _(flag: bool):
1102
1182
def inner1 (a_and_b : Intersection[A1, B1]):
1103
1183
# error: [possibly-unbound-attribute]
1104
1184
reveal_type(a_and_b.x) # revealed: P
1185
+
1186
+ # error: [possibly-unbound-attribute]
1187
+ a_and_b.x = R()
1105
1188
# Same for class objects
1106
1189
def inner1_class (a_and_b : Intersection[type[A1], type[B1]]):
1107
1190
# error: [possibly-unbound-attribute]
1108
1191
reveal_type(a_and_b.x) # revealed: P
1109
1192
1193
+ # error: [possibly-unbound-attribute]
1194
+ a_and_b.x = R()
1195
+
1110
1196
class A2 :
1111
1197
if flag:
1112
1198
x: P = P()
@@ -1116,6 +1202,11 @@ def _(flag: bool):
1116
1202
1117
1203
def inner2 (a_and_b : Intersection[A2, B1]):
1118
1204
reveal_type(a_and_b.x) # revealed: P & Q
1205
+
1206
+ # TODO : this should not be an error, we need better intersection
1207
+ # handling in `validate_attribute_assignment` for this
1208
+ # error: [possibly-unbound-attribute]
1209
+ a_and_b.x = R()
1119
1210
# Same for class objects
1120
1211
def inner2_class (a_and_b : Intersection[type[A2], type[B1]]):
1121
1212
reveal_type(a_and_b.x) # revealed: P & Q
@@ -1131,21 +1222,33 @@ def _(flag: bool):
1131
1222
def inner3 (a_and_b : Intersection[A3, B3]):
1132
1223
# error: [possibly-unbound-attribute]
1133
1224
reveal_type(a_and_b.x) # revealed: P & Q
1225
+
1226
+ # error: [possibly-unbound-attribute]
1227
+ a_and_b.x = R()
1134
1228
# Same for class objects
1135
1229
def inner3_class (a_and_b : Intersection[type[A3], type[B3]]):
1136
1230
# error: [possibly-unbound-attribute]
1137
1231
reveal_type(a_and_b.x) # revealed: P & Q
1138
1232
1233
+ # error: [possibly-unbound-attribute]
1234
+ a_and_b.x = R()
1235
+
1139
1236
class A4 : ...
1140
1237
class B4 : ...
1141
1238
1142
1239
def inner4 (a_and_b : Intersection[A4, B4]):
1143
1240
# error: [unresolved-attribute]
1144
1241
reveal_type(a_and_b.x) # revealed: Unknown
1242
+
1243
+ # error: [invalid-assignment]
1244
+ a_and_b.x = R()
1145
1245
# Same for class objects
1146
1246
def inner4_class (a_and_b : Intersection[type[A4], type[B4]]):
1147
1247
# error: [unresolved-attribute]
1148
1248
reveal_type(a_and_b.x) # revealed: Unknown
1249
+
1250
+ # error: [invalid-assignment]
1251
+ a_and_b.x = R()
1149
1252
```
1150
1253
1151
1254
### Intersection of implicit instance attributes
0 commit comments