Files
ruff/crates/red_knot_python_semantic/resources/mdtest/generics.md
Raphael Gaschignard 3d0bdb426a [red-knot] Use the right scope when considering class bases (#13766)
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>
2024-10-17 22:29:46 +00:00

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]