Skip to content

Commit c034e28

Browse files
authored
[red-knot] Instance attributes: type inference clarifications (#15512)
## Summary Some clarifications in the instance-attributes tests, mostly regarding type inference behavior following this discussion: #15474 (comment)
1 parent 49557a9 commit c034e28

File tree

1 file changed

+25
-16
lines changed

1 file changed

+25
-16
lines changed

crates/red_knot_python_semantic/resources/mdtest/attributes.md

+25-16
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ class C:
3232

3333
c_instance = C(1)
3434

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.
3637
reveal_type(c_instance.pure_instance_variable1) # revealed: @Todo(instance attributes)
3738

3839
# TODO: should be `int`
@@ -41,14 +42,17 @@ reveal_type(c_instance.pure_instance_variable2) # revealed: @Todo(instance attr
4142
# TODO: should be `bytes`
4243
reveal_type(c_instance.pure_instance_variable3) # revealed: @Todo(instance attributes)
4344

44-
# TODO: should be `Literal[True]` (or `bool`)
45+
# TODO: should be `bool`
4546
reveal_type(c_instance.pure_instance_variable4) # revealed: @Todo(instance attributes)
4647

47-
# TODO: should be `Literal["possibly set in __init__"]` (or `str`)
48+
# TODO: should be `str`
4849
# We probably don't want to emit a diagnostic for this being possibly undeclared/unbound.
4950
# mypy and pyright do not show an error here.
5051
reveal_type(c_instance.pure_instance_variable5) # revealed: @Todo(instance attributes)
5152

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.
5256
c_instance.pure_instance_variable1 = "value set on instance"
5357

5458
# TODO: this should be an error (incompatible types in assignment)
@@ -63,9 +67,14 @@ reveal_type(C.pure_instance_variable1) # revealed: Unknown
6367
# mypy shows no error here, but pyright raises "reportAttributeAccessIssue"
6468
C.pure_instance_variable1 = "overwritten on class"
6569

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)
6978
```
7079

7180
#### Variable declared in class body and declared/bound in `__init__`
@@ -109,10 +118,11 @@ class C:
109118

110119
c_instance = C()
111120

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.
113123
c_instance.set_instance_variable()
114124

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).
116126
reveal_type(c_instance.pure_instance_variable) # revealed: @Todo(instance attributes)
117127

118128
# TODO: We already show an error here, but the message might be improved?
@@ -163,7 +173,7 @@ class C:
163173

164174
reveal_type(C.pure_class_variable1) # revealed: str
165175

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]`.
167177
reveal_type(C.pure_class_variable2) # revealed: @Todo(Unsupported or invalid type in a type expression)
168178

169179
c_instance = C()
@@ -176,9 +186,6 @@ c_instance.pure_class_variable1 = "value set on instance"
176186

177187
C.pure_class_variable1 = "overwritten on class"
178188

179-
# TODO: should ideally be `Literal["overwritten on class"]`, but not a priority
180-
reveal_type(C.pure_class_variable1) # revealed: str
181-
182189
# TODO: should raise an error (incompatible types in assignment)
183190
C.pure_class_variable1 = 1
184191

@@ -214,7 +221,7 @@ C.pure_class_variable = "overwritten on class"
214221
reveal_type(C.pure_class_variable) # revealed: Unknown
215222

216223
c_instance = C()
217-
# TODO: should be `Literal["overwritten on class"]` or `str`
224+
# TODO: should be `Literal["overwritten on class"]`
218225
reveal_type(c_instance.pure_class_variable) # revealed: @Todo(instance attributes)
219226

220227
# TODO: should raise an error.
@@ -248,15 +255,17 @@ c_instance.variable_with_class_default = "value set on instance"
248255

249256
reveal_type(C.variable_with_class_default) # revealed: str
250257

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.
252260
reveal_type(c_instance.variable_with_class_default) # revealed: @Todo(instance attributes)
253261

254262
C.variable_with_class_default = "overwritten on class"
255263

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.
257266
reveal_type(C.variable_with_class_default) # revealed: str
258267

259-
# TODO: should still be `Literal["value set on instance"]`
268+
# TODO: should still be `Literal["value set on instance"]`, or `str`.
260269
reveal_type(c_instance.variable_with_class_default) # revealed: @Todo(instance attributes)
261270
```
262271

0 commit comments

Comments
 (0)