mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-03 07:04:53 +00:00

This PR lets you explicitly specialize a generic class using a subscript expression. It introduces three new Rust types for representing classes: - `NonGenericClass` - `GenericClass` (not specialized) - `GenericAlias` (specialized) and two enum wrappers: - `ClassType` (a non-generic class or generic alias, represents a class _type_ at runtime) - `ClassLiteralType` (a non-generic class or generic class, represents a class body in the AST) We also add internal support for specializing callables, in particular function literals. (That is, the internal `Type` representation now attaches an optional specialization to a function literal.) This is used in this PR for the methods of a generic class, but should also give us most of what we need for specializing generic _functions_ (which this PR does not yet tackle). --------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com> Co-authored-by: Carl Meyer <carl@astral.sh>
1.2 KiB
1.2 KiB
Class definitions in stubs
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]): ...
.
class Foo[T]: ...
class Bar(Foo[Bar]): ...
reveal_type(Bar) # revealed: Literal[Bar]
reveal_type(Bar.__mro__) # revealed: tuple[Literal[Bar], Literal[Foo[Bar]], Literal[object]]
Access to attributes declared in stubs
Unlike regular Python modules, stub files often omit the right-hand side in declarations, including
in class scope. However, from the perspective of the type checker, we have to treat them as bindings
too. That is, symbol: type
is the same as symbol: type = ...
.
One implication of this is that we'll always treat symbols in class scope as safe to be accessed from the class object itself. We'll never infer a "pure instance attribute" from a stub.
b.pyi
:
from typing import ClassVar
class C:
class_or_instance_var: int
from typing import ClassVar, Literal
from b import C
# No error here, since we treat `class_or_instance_var` as bound on the class.
reveal_type(C.class_or_instance_var) # revealed: int