@@ -32,7 +32,8 @@ class C:
32
32
33
33
c_instance = C(1 )
34
34
35
- # TODO : should be `Literal["value set in __init__"]` (or `str` which would probably be more generally useful)
35
+ # TODO : should be `Literal["value set in __init__"]`, or `Unknown | Literal[…]` to allow
36
+ # assignments to this unannotated attribute from other scopes.
36
37
reveal_type(c_instance.pure_instance_variable1) # revealed: @Todo(instance attributes)
37
38
38
39
# TODO : should be `int`
@@ -41,14 +42,17 @@ reveal_type(c_instance.pure_instance_variable2) # revealed: @Todo(instance attr
41
42
# TODO : should be `bytes`
42
43
reveal_type(c_instance.pure_instance_variable3) # revealed: @Todo(instance attributes)
43
44
44
- # TODO : should be `Literal[True]` (or ` bool`)
45
+ # TODO : should be `bool`
45
46
reveal_type(c_instance.pure_instance_variable4) # revealed: @Todo(instance attributes)
46
47
47
- # TODO : should be `Literal["possibly set in __init__"]` (or ` str`)
48
+ # TODO : should be `str`
48
49
# We probably don't want to emit a diagnostic for this being possibly undeclared/unbound.
49
50
# mypy and pyright do not show an error here.
50
51
reveal_type(c_instance.pure_instance_variable5) # revealed: @Todo(instance attributes)
51
52
53
+ # TODO : If we choose to infer a precise `Literal[…]` type for the instance attribute (see
54
+ # above), this should be an error: incompatible types in assignment. If we choose to infer
55
+ # a gradual `Unknown | Literal[…]` type, this assignment is fine.
52
56
c_instance.pure_instance_variable1 = " value set on instance"
53
57
54
58
# TODO : this should be an error (incompatible types in assignment)
@@ -63,9 +67,14 @@ reveal_type(C.pure_instance_variable1) # revealed: Unknown
63
67
# mypy shows no error here, but pyright raises "reportAttributeAccessIssue"
64
68
C.pure_instance_variable1 = " overwritten on class"
65
69
66
- # TODO : should ideally be `Literal["value set on instance"]`
67
- # (due to earlier assignment of the attribute from the global scope)
68
- reveal_type(c_instance.pure_instance_variable1) # revealed: @Todo(instance attributes)
70
+ c_instance.pure_instance_variable4 = False
71
+
72
+ # TODO : After this assignment to the attribute within this scope, we may eventually want to narrow
73
+ # the `bool` type (see above) for this instance variable to `Literal[False]` here. This is unsound
74
+ # in general (we don't know what else happened to `c_instance` between the assignment and the use
75
+ # here), but mypy and pyright support this. In conclusion, this could be `bool` but should probably
76
+ # be `Literal[False]`.
77
+ reveal_type(c_instance.pure_instance_variable4) # revealed: @Todo(instance attributes)
69
78
```
70
79
71
80
#### Variable declared in class body and declared/bound in ` __init__ `
@@ -109,10 +118,11 @@ class C:
109
118
110
119
c_instance = C()
111
120
112
- # for a more realistic example, let's actually call the method
121
+ # Not that we would use this in static analysis, but for a more realistic example, let's actually
122
+ # call the method, so that the attribute is bound if this example is actually run.
113
123
c_instance.set_instance_variable()
114
124
115
- # TODO : should be `Literal["value set in method"]` or `str`
125
+ # TODO : should be `Literal["value set in method"]` or `Unknown | Literal[…]` (see above).
116
126
reveal_type(c_instance.pure_instance_variable) # revealed: @Todo(instance attributes)
117
127
118
128
# TODO : We already show an error here, but the message might be improved?
@@ -163,7 +173,7 @@ class C:
163
173
164
174
reveal_type(C.pure_class_variable1) # revealed: str
165
175
166
- # TODO : this should be `Literal[1]`, `int`, or maybe `Unknown | Literal[1]` / `Unknown | int`
176
+ # TODO : this should be `Literal[1]`, or `Unknown | Literal[1]`.
167
177
reveal_type(C.pure_class_variable2) # revealed: @Todo(Unsupported or invalid type in a type expression)
168
178
169
179
c_instance = C()
@@ -176,9 +186,6 @@ c_instance.pure_class_variable1 = "value set on instance"
176
186
177
187
C.pure_class_variable1 = " overwritten on class"
178
188
179
- # TODO : should ideally be `Literal["overwritten on class"]`, but not a priority
180
- reveal_type(C.pure_class_variable1) # revealed: str
181
-
182
189
# TODO : should raise an error (incompatible types in assignment)
183
190
C.pure_class_variable1 = 1
184
191
@@ -214,7 +221,7 @@ C.pure_class_variable = "overwritten on class"
214
221
reveal_type(C.pure_class_variable) # revealed: Unknown
215
222
216
223
c_instance = C()
217
- # TODO : should be `Literal["overwritten on class"]` or `str`
224
+ # TODO : should be `Literal["overwritten on class"]`
218
225
reveal_type(c_instance.pure_class_variable) # revealed: @Todo(instance attributes)
219
226
220
227
# TODO : should raise an error.
@@ -248,15 +255,17 @@ c_instance.variable_with_class_default = "value set on instance"
248
255
249
256
reveal_type(C.variable_with_class_default) # revealed: str
250
257
251
- # TODO : should ideally be Literal["value set on instance"], or still `str`
258
+ # TODO : Could be Literal["value set on instance"], or still `str` if we choose not to
259
+ # narrow the type.
252
260
reveal_type(c_instance.variable_with_class_default) # revealed: @Todo(instance attributes)
253
261
254
262
C.variable_with_class_default = " overwritten on class"
255
263
256
- # TODO : should ideally be `Literal["overwritten on class"]`
264
+ # TODO : Could be `Literal["overwritten on class"]`, or still `str` if we choose not to
265
+ # narrow the type.
257
266
reveal_type(C.variable_with_class_default) # revealed: str
258
267
259
- # TODO : should still be `Literal["value set on instance"]`
268
+ # TODO : should still be `Literal["value set on instance"]`, or `str`.
260
269
reveal_type(c_instance.variable_with_class_default) # revealed: @Todo(instance attributes)
261
270
```
262
271
0 commit comments