Summary --------- PEP 695 Generics introduce a scope inside a class statement's arguments and keywords. ``` class C[T](A[T]): # the T in A[T] is not from the global scope but from a type-param-specfic scope ... ``` When doing inference on the class bases, we currently have been doing base class expression lookups in the global scope. Not an issue without generics (since a scope is only created when generics are present). This change instead makes sure to stop the global scope inference from going into expressions within this sub-scope. Since there is a separate scope, `check_file` and friends will trigger inference on these expressions still. Another change as a part of this is making sure that `ClassType` looks up its bases in the right scope. Test Plan ---------- `cargo test --package red_knot_python_semantic generics` will run the markdown test that previously would panic due to scope lookup issues --------- Co-authored-by: Micha Reiser <micha@reiser.io> Co-authored-by: Carl Meyer <carl@astral.sh>
1.3 KiB
1.3 KiB
PEP 695 Generics
Class Declarations
Basic PEP 695 generics
class MyBox[T]:
data: T
box_model_number = 695
def __init__(self, data: T):
self.data = data
# TODO not error (should be subscriptable)
box: MyBox[int] = MyBox(5) # error: [non-subscriptable]
# TODO error differently (str and int don't unify)
wrong_innards: MyBox[int] = MyBox("five") # error: [non-subscriptable]
# TODO reveal int
reveal_type(box.data) # revealed: @Todo
reveal_type(MyBox.box_model_number) # revealed: Literal[695]
Subclassing
class MyBox[T]:
data: T
def __init__(self, data: T):
self.data = data
# TODO not error on the subscripting
class MySecureBox[T](MyBox[T]): # error: [non-subscriptable]
pass
secure_box: MySecureBox[int] = MySecureBox(5)
reveal_type(secure_box) # revealed: MySecureBox
# TODO reveal int
reveal_type(secure_box.data) # revealed: @Todo
Cyclical class definition
In type stubs, classes can reference themselves in their base class definitions. For example, in typeshed, we have class str(Sequence[str]): ....
This should hold true even with generics at play.
class Seq[T]:
pass
# TODO not error on the subscripting
class S[T](Seq[S]): # error: [non-subscriptable]
pass
reveal_type(S) # revealed: Literal[S]