mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-02 21:03:11 +00:00
[ty] Track different uses of legacy typevars, including context when rendering typevars (#19604)
Some checks are pending
CI / mkdocs (push) Waiting to run
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
Some checks are pending
CI / mkdocs (push) Waiting to run
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
This PR introduces a few related changes: - We now keep track of each time a legacy typevar is bound in a different generic context (e.g. class, function), and internally create a new `TypeVarInstance` for each usage. This means the rest of the code can now assume that salsa-equivalent `TypeVarInstance`s refer to the same typevar, even taking into account that legacy typevars can be used more than once. - We also go ahead and track the binding context of PEP 695 typevars. That's _much_ easier to track since we have the binding context right there during type inference. - With that in place, we can now include the name of the binding context when rendering typevars (e.g. `T@f` instead of `T`)
This commit is contained in:
parent
48d5bd13fa
commit
06cd249a9b
28 changed files with 394 additions and 128 deletions
|
|
@ -15,8 +15,10 @@ S = TypeVar("S")
|
|||
class SingleTypevar(Generic[T]): ...
|
||||
class MultipleTypevars(Generic[T, S]): ...
|
||||
|
||||
reveal_type(generic_context(SingleTypevar)) # revealed: tuple[T]
|
||||
reveal_type(generic_context(MultipleTypevars)) # revealed: tuple[T, S]
|
||||
# revealed: tuple[T@SingleTypevar]
|
||||
reveal_type(generic_context(SingleTypevar))
|
||||
# revealed: tuple[T@MultipleTypevars, S@MultipleTypevars]
|
||||
reveal_type(generic_context(MultipleTypevars))
|
||||
```
|
||||
|
||||
Inheriting from `Generic` multiple times yields a `duplicate-base` diagnostic, just like any other
|
||||
|
|
@ -49,9 +51,12 @@ class InheritedGeneric(MultipleTypevars[T, S]): ...
|
|||
class InheritedGenericPartiallySpecialized(MultipleTypevars[T, int]): ...
|
||||
class InheritedGenericFullySpecialized(MultipleTypevars[str, int]): ...
|
||||
|
||||
reveal_type(generic_context(InheritedGeneric)) # revealed: tuple[T, S]
|
||||
reveal_type(generic_context(InheritedGenericPartiallySpecialized)) # revealed: tuple[T]
|
||||
reveal_type(generic_context(InheritedGenericFullySpecialized)) # revealed: None
|
||||
# revealed: tuple[T@InheritedGeneric, S@InheritedGeneric]
|
||||
reveal_type(generic_context(InheritedGeneric))
|
||||
# revealed: tuple[T@InheritedGenericPartiallySpecialized]
|
||||
reveal_type(generic_context(InheritedGenericPartiallySpecialized))
|
||||
# revealed: None
|
||||
reveal_type(generic_context(InheritedGenericFullySpecialized))
|
||||
```
|
||||
|
||||
If you don't specialize a generic base class, we use the default specialization, which maps each
|
||||
|
|
@ -78,9 +83,12 @@ class ExplicitInheritedGenericPartiallySpecializedExtraTypevar(MultipleTypevars[
|
|||
# error: [invalid-generic-class] "`Generic` base class must include all type variables used in other base classes"
|
||||
class ExplicitInheritedGenericPartiallySpecializedMissingTypevar(MultipleTypevars[T, int], Generic[S]): ...
|
||||
|
||||
reveal_type(generic_context(ExplicitInheritedGeneric)) # revealed: tuple[T, S]
|
||||
reveal_type(generic_context(ExplicitInheritedGenericPartiallySpecialized)) # revealed: tuple[T]
|
||||
reveal_type(generic_context(ExplicitInheritedGenericPartiallySpecializedExtraTypevar)) # revealed: tuple[T, S]
|
||||
# revealed: tuple[T@ExplicitInheritedGeneric, S@ExplicitInheritedGeneric]
|
||||
reveal_type(generic_context(ExplicitInheritedGeneric))
|
||||
# revealed: tuple[T@ExplicitInheritedGenericPartiallySpecialized]
|
||||
reveal_type(generic_context(ExplicitInheritedGenericPartiallySpecialized))
|
||||
# revealed: tuple[T@ExplicitInheritedGenericPartiallySpecializedExtraTypevar, S@ExplicitInheritedGenericPartiallySpecializedExtraTypevar]
|
||||
reveal_type(generic_context(ExplicitInheritedGenericPartiallySpecializedExtraTypevar))
|
||||
```
|
||||
|
||||
## Specializing generic classes explicitly
|
||||
|
|
@ -446,18 +454,18 @@ class C(Generic[T]):
|
|||
def generic_method(self, t: T, u: U) -> U:
|
||||
return u
|
||||
|
||||
reveal_type(generic_context(C)) # revealed: tuple[T]
|
||||
reveal_type(generic_context(C)) # revealed: tuple[T@C]
|
||||
reveal_type(generic_context(C.method)) # revealed: None
|
||||
reveal_type(generic_context(C.generic_method)) # revealed: tuple[U]
|
||||
reveal_type(generic_context(C.generic_method)) # revealed: tuple[U@generic_method]
|
||||
reveal_type(generic_context(C[int])) # revealed: None
|
||||
reveal_type(generic_context(C[int].method)) # revealed: None
|
||||
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[U]
|
||||
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[U@generic_method]
|
||||
|
||||
c: C[int] = C[int]()
|
||||
reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"]
|
||||
reveal_type(generic_context(c)) # revealed: None
|
||||
reveal_type(generic_context(c.method)) # revealed: None
|
||||
reveal_type(generic_context(c.generic_method)) # revealed: tuple[U]
|
||||
reveal_type(generic_context(c.generic_method)) # revealed: tuple[U@generic_method]
|
||||
```
|
||||
|
||||
## Specializations propagate
|
||||
|
|
@ -540,7 +548,8 @@ class WithOverloadedMethod(Generic[T]):
|
|||
def method(self, x: S | T) -> S | T:
|
||||
return x
|
||||
|
||||
reveal_type(WithOverloadedMethod[int].method) # revealed: Overload[(self, x: int) -> int, (self, x: S) -> S | int]
|
||||
# revealed: Overload[(self, x: int) -> int, (self, x: S@method) -> S@method | int]
|
||||
reveal_type(WithOverloadedMethod[int].method)
|
||||
```
|
||||
|
||||
## Cyclic class definitions
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ from typing import TypeVar
|
|||
T = TypeVar("T", bound=int)
|
||||
|
||||
def good_param(x: T) -> None:
|
||||
reveal_type(x) # revealed: T
|
||||
reveal_type(x) # revealed: T@good_param
|
||||
```
|
||||
|
||||
If the function is annotated as returning the typevar, this means that the upper bound is _not_
|
||||
|
|
@ -239,7 +239,7 @@ def good_return(x: T) -> T:
|
|||
return x
|
||||
|
||||
def bad_return(x: T) -> T:
|
||||
# error: [invalid-return-type] "Return type does not match returned value: expected `T`, found `int`"
|
||||
# error: [invalid-return-type] "Return type does not match returned value: expected `T@bad_return`, found `int`"
|
||||
return x + 1
|
||||
```
|
||||
|
||||
|
|
@ -257,7 +257,7 @@ def different_types(cond: bool, t: T, s: S) -> T:
|
|||
if cond:
|
||||
return t
|
||||
else:
|
||||
# error: [invalid-return-type] "Return type does not match returned value: expected `T`, found `S`"
|
||||
# error: [invalid-return-type] "Return type does not match returned value: expected `T@different_types`, found `S@different_types`"
|
||||
return s
|
||||
|
||||
def same_types(cond: bool, t1: T, t2: T) -> T:
|
||||
|
|
@ -279,7 +279,7 @@ T = TypeVar("T", int, str)
|
|||
|
||||
def same_constrained_types(t1: T, t2: T) -> T:
|
||||
# TODO: no error
|
||||
# error: [unsupported-operator] "Operator `+` is unsupported between objects of type `T` and `T`"
|
||||
# error: [unsupported-operator] "Operator `+` is unsupported between objects of type `T@same_constrained_types` and `T@same_constrained_types`"
|
||||
return t1 + t2
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ from typing import Callable, TypeVar
|
|||
T = TypeVar("T", bound=Callable[[], int])
|
||||
|
||||
def bound(f: T):
|
||||
reveal_type(f) # revealed: T
|
||||
reveal_type(f) # revealed: T@bound
|
||||
reveal_type(f()) # revealed: int
|
||||
```
|
||||
|
||||
|
|
@ -192,7 +192,7 @@ Same with a constrained typevar, as long as all constraints are callable:
|
|||
T = TypeVar("T", Callable[[], int], Callable[[], str])
|
||||
|
||||
def constrained(f: T):
|
||||
reveal_type(f) # revealed: T
|
||||
reveal_type(f) # revealed: T@constrained
|
||||
reveal_type(f()) # revealed: int | str
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@ from ty_extensions import generic_context
|
|||
class SingleTypevar[T]: ...
|
||||
class MultipleTypevars[T, S]: ...
|
||||
|
||||
reveal_type(generic_context(SingleTypevar)) # revealed: tuple[T]
|
||||
reveal_type(generic_context(MultipleTypevars)) # revealed: tuple[T, S]
|
||||
# revealed: tuple[T@SingleTypevar]
|
||||
reveal_type(generic_context(SingleTypevar))
|
||||
# revealed: tuple[T@MultipleTypevars, S@MultipleTypevars]
|
||||
reveal_type(generic_context(MultipleTypevars))
|
||||
```
|
||||
|
||||
You cannot use the same typevar more than once.
|
||||
|
|
@ -43,9 +45,12 @@ class InheritedGeneric[U, V](MultipleTypevars[U, V]): ...
|
|||
class InheritedGenericPartiallySpecialized[U](MultipleTypevars[U, int]): ...
|
||||
class InheritedGenericFullySpecialized(MultipleTypevars[str, int]): ...
|
||||
|
||||
reveal_type(generic_context(InheritedGeneric)) # revealed: tuple[U, V]
|
||||
reveal_type(generic_context(InheritedGenericPartiallySpecialized)) # revealed: tuple[U]
|
||||
reveal_type(generic_context(InheritedGenericFullySpecialized)) # revealed: None
|
||||
# revealed: tuple[U@InheritedGeneric, V@InheritedGeneric]
|
||||
reveal_type(generic_context(InheritedGeneric))
|
||||
# revealed: tuple[U@InheritedGenericPartiallySpecialized]
|
||||
reveal_type(generic_context(InheritedGenericPartiallySpecialized))
|
||||
# revealed: None
|
||||
reveal_type(generic_context(InheritedGenericFullySpecialized))
|
||||
```
|
||||
|
||||
If you don't specialize a generic base class, we use the default specialization, which maps each
|
||||
|
|
@ -406,18 +411,18 @@ class C[T]:
|
|||
# TODO: error
|
||||
def cannot_shadow_class_typevar[T](self, t: T): ...
|
||||
|
||||
reveal_type(generic_context(C)) # revealed: tuple[T]
|
||||
reveal_type(generic_context(C)) # revealed: tuple[T@C]
|
||||
reveal_type(generic_context(C.method)) # revealed: None
|
||||
reveal_type(generic_context(C.generic_method)) # revealed: tuple[U]
|
||||
reveal_type(generic_context(C.generic_method)) # revealed: tuple[U@generic_method]
|
||||
reveal_type(generic_context(C[int])) # revealed: None
|
||||
reveal_type(generic_context(C[int].method)) # revealed: None
|
||||
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[U]
|
||||
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[U@generic_method]
|
||||
|
||||
c: C[int] = C[int]()
|
||||
reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"]
|
||||
reveal_type(generic_context(c)) # revealed: None
|
||||
reveal_type(generic_context(c.method)) # revealed: None
|
||||
reveal_type(generic_context(c.generic_method)) # revealed: tuple[U]
|
||||
reveal_type(generic_context(c.generic_method)) # revealed: tuple[U@generic_method]
|
||||
```
|
||||
|
||||
## Specializations propagate
|
||||
|
|
@ -466,7 +471,8 @@ class WithOverloadedMethod[T]:
|
|||
def method[S](self, x: S | T) -> S | T:
|
||||
return x
|
||||
|
||||
reveal_type(WithOverloadedMethod[int].method) # revealed: Overload[(self, x: int) -> int, (self, x: S) -> S | int]
|
||||
# revealed: Overload[(self, x: int) -> int, (self, x: S@method) -> S@method | int]
|
||||
reveal_type(WithOverloadedMethod[int].method)
|
||||
```
|
||||
|
||||
## Cyclic class definitions
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ in the function.
|
|||
|
||||
```py
|
||||
def good_param[T: int](x: T) -> None:
|
||||
reveal_type(x) # revealed: T
|
||||
reveal_type(x) # revealed: T@good_param
|
||||
```
|
||||
|
||||
If the function is annotated as returning the typevar, this means that the upper bound is _not_
|
||||
|
|
@ -215,7 +215,7 @@ def good_return[T: int](x: T) -> T:
|
|||
return x
|
||||
|
||||
def bad_return[T: int](x: T) -> T:
|
||||
# error: [invalid-return-type] "Return type does not match returned value: expected `T`, found `int`"
|
||||
# error: [invalid-return-type] "Return type does not match returned value: expected `T@bad_return`, found `int`"
|
||||
return x + 1
|
||||
```
|
||||
|
||||
|
|
@ -228,7 +228,7 @@ def different_types[T, S](cond: bool, t: T, s: S) -> T:
|
|||
if cond:
|
||||
return t
|
||||
else:
|
||||
# error: [invalid-return-type] "Return type does not match returned value: expected `T`, found `S`"
|
||||
# error: [invalid-return-type] "Return type does not match returned value: expected `T@different_types`, found `S@different_types`"
|
||||
return s
|
||||
|
||||
def same_types[T](cond: bool, t1: T, t2: T) -> T:
|
||||
|
|
@ -246,7 +246,7 @@ methods that are compatible with the return type, so the `return` expression is
|
|||
```py
|
||||
def same_constrained_types[T: (int, str)](t1: T, t2: T) -> T:
|
||||
# TODO: no error
|
||||
# error: [unsupported-operator] "Operator `+` is unsupported between objects of type `T` and `T`"
|
||||
# error: [unsupported-operator] "Operator `+` is unsupported between objects of type `T@same_constrained_types` and `T@same_constrained_types`"
|
||||
return t1 + t2
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -104,13 +104,11 @@ different uses of the same typevar.
|
|||
|
||||
```py
|
||||
def f[T](x: T, y: T) -> None:
|
||||
# TODO: revealed: T@f
|
||||
reveal_type(x) # revealed: T
|
||||
reveal_type(x) # revealed: T@f
|
||||
|
||||
class C[T]:
|
||||
def m(self, x: T) -> None:
|
||||
# TODO: revealed: T@c
|
||||
reveal_type(x) # revealed: T
|
||||
reveal_type(x) # revealed: T@C
|
||||
```
|
||||
|
||||
## Subtyping and assignability
|
||||
|
|
@ -452,19 +450,19 @@ class Unrelated: ...
|
|||
|
||||
def unbounded_unconstrained[T](t: T) -> None:
|
||||
def _(x: T | Super) -> None:
|
||||
reveal_type(x) # revealed: T | Super
|
||||
reveal_type(x) # revealed: T@unbounded_unconstrained | Super
|
||||
|
||||
def _(x: T | Base) -> None:
|
||||
reveal_type(x) # revealed: T | Base
|
||||
reveal_type(x) # revealed: T@unbounded_unconstrained | Base
|
||||
|
||||
def _(x: T | Sub) -> None:
|
||||
reveal_type(x) # revealed: T | Sub
|
||||
reveal_type(x) # revealed: T@unbounded_unconstrained | Sub
|
||||
|
||||
def _(x: T | Unrelated) -> None:
|
||||
reveal_type(x) # revealed: T | Unrelated
|
||||
reveal_type(x) # revealed: T@unbounded_unconstrained | Unrelated
|
||||
|
||||
def _(x: T | Any) -> None:
|
||||
reveal_type(x) # revealed: T | Any
|
||||
reveal_type(x) # revealed: T@unbounded_unconstrained | Any
|
||||
```
|
||||
|
||||
The union of a bounded typevar with its bound is that bound. (The typevar is guaranteed to be
|
||||
|
|
@ -480,13 +478,13 @@ def bounded[T: Base](t: T) -> None:
|
|||
reveal_type(x) # revealed: Base
|
||||
|
||||
def _(x: T | Sub) -> None:
|
||||
reveal_type(x) # revealed: T | Sub
|
||||
reveal_type(x) # revealed: T@bounded | Sub
|
||||
|
||||
def _(x: T | Unrelated) -> None:
|
||||
reveal_type(x) # revealed: T | Unrelated
|
||||
reveal_type(x) # revealed: T@bounded | Unrelated
|
||||
|
||||
def _(x: T | Any) -> None:
|
||||
reveal_type(x) # revealed: T | Any
|
||||
reveal_type(x) # revealed: T@bounded | Any
|
||||
```
|
||||
|
||||
The union of a constrained typevar with a type depends on how that type relates to the constraints.
|
||||
|
|
@ -503,13 +501,13 @@ def constrained[T: (Base, Sub)](t: T) -> None:
|
|||
reveal_type(x) # revealed: Base
|
||||
|
||||
def _(x: T | Sub) -> None:
|
||||
reveal_type(x) # revealed: T
|
||||
reveal_type(x) # revealed: T@constrained
|
||||
|
||||
def _(x: T | Unrelated) -> None:
|
||||
reveal_type(x) # revealed: T | Unrelated
|
||||
reveal_type(x) # revealed: T@constrained | Unrelated
|
||||
|
||||
def _(x: T | Any) -> None:
|
||||
reveal_type(x) # revealed: T | Any
|
||||
reveal_type(x) # revealed: T@constrained | Any
|
||||
```
|
||||
|
||||
## Intersections involving typevars
|
||||
|
|
@ -528,19 +526,19 @@ class Unrelated: ...
|
|||
|
||||
def unbounded_unconstrained[T](t: T) -> None:
|
||||
def _(x: Intersection[T, Super]) -> None:
|
||||
reveal_type(x) # revealed: T & Super
|
||||
reveal_type(x) # revealed: T@unbounded_unconstrained & Super
|
||||
|
||||
def _(x: Intersection[T, Base]) -> None:
|
||||
reveal_type(x) # revealed: T & Base
|
||||
reveal_type(x) # revealed: T@unbounded_unconstrained & Base
|
||||
|
||||
def _(x: Intersection[T, Sub]) -> None:
|
||||
reveal_type(x) # revealed: T & Sub
|
||||
reveal_type(x) # revealed: T@unbounded_unconstrained & Sub
|
||||
|
||||
def _(x: Intersection[T, Unrelated]) -> None:
|
||||
reveal_type(x) # revealed: T & Unrelated
|
||||
reveal_type(x) # revealed: T@unbounded_unconstrained & Unrelated
|
||||
|
||||
def _(x: Intersection[T, Any]) -> None:
|
||||
reveal_type(x) # revealed: T & Any
|
||||
reveal_type(x) # revealed: T@unbounded_unconstrained & Any
|
||||
```
|
||||
|
||||
The intersection of a bounded typevar with its bound or a supertype of its bound is the typevar
|
||||
|
|
@ -552,19 +550,19 @@ from its bound is `Never`.
|
|||
```py
|
||||
def bounded[T: Base](t: T) -> None:
|
||||
def _(x: Intersection[T, Super]) -> None:
|
||||
reveal_type(x) # revealed: T
|
||||
reveal_type(x) # revealed: T@bounded
|
||||
|
||||
def _(x: Intersection[T, Base]) -> None:
|
||||
reveal_type(x) # revealed: T
|
||||
reveal_type(x) # revealed: T@bounded
|
||||
|
||||
def _(x: Intersection[T, Sub]) -> None:
|
||||
reveal_type(x) # revealed: T & Sub
|
||||
reveal_type(x) # revealed: T@bounded & Sub
|
||||
|
||||
def _(x: Intersection[T, None]) -> None:
|
||||
reveal_type(x) # revealed: Never
|
||||
|
||||
def _(x: Intersection[T, Any]) -> None:
|
||||
reveal_type(x) # revealed: T & Any
|
||||
reveal_type(x) # revealed: T@bounded & Any
|
||||
```
|
||||
|
||||
Constrained typevars can be modeled using a hypothetical `OneOf` connector, where the typevar must
|
||||
|
|
@ -586,7 +584,7 @@ can simplify the intersection as a whole to that constraint.
|
|||
def constrained[T: (Base, Sub, Unrelated)](t: T) -> None:
|
||||
def _(x: Intersection[T, Base]) -> None:
|
||||
# With OneOf this would be OneOf[Base, Sub]
|
||||
reveal_type(x) # revealed: T & Base
|
||||
reveal_type(x) # revealed: T@constrained & Base
|
||||
|
||||
def _(x: Intersection[T, Unrelated]) -> None:
|
||||
reveal_type(x) # revealed: Unrelated
|
||||
|
|
@ -598,7 +596,7 @@ def constrained[T: (Base, Sub, Unrelated)](t: T) -> None:
|
|||
reveal_type(x) # revealed: Never
|
||||
|
||||
def _(x: Intersection[T, Any]) -> None:
|
||||
reveal_type(x) # revealed: T & Any
|
||||
reveal_type(x) # revealed: T@constrained & Any
|
||||
```
|
||||
|
||||
We can simplify the intersection similarly when removing a type from a constrained typevar, since
|
||||
|
|
@ -613,19 +611,19 @@ def remove_constraint[T: (int, str, bool)](t: T) -> None:
|
|||
|
||||
def _(x: Intersection[T, Not[str]]) -> None:
|
||||
# With OneOf this would be OneOf[int, bool]
|
||||
reveal_type(x) # revealed: T & ~str
|
||||
reveal_type(x) # revealed: T@remove_constraint & ~str
|
||||
|
||||
def _(x: Intersection[T, Not[bool]]) -> None:
|
||||
reveal_type(x) # revealed: T & ~bool
|
||||
reveal_type(x) # revealed: T@remove_constraint & ~bool
|
||||
|
||||
def _(x: Intersection[T, Not[int], Not[str]]) -> None:
|
||||
reveal_type(x) # revealed: Never
|
||||
|
||||
def _(x: Intersection[T, Not[None]]) -> None:
|
||||
reveal_type(x) # revealed: T
|
||||
reveal_type(x) # revealed: T@remove_constraint
|
||||
|
||||
def _(x: Intersection[T, Not[Any]]) -> None:
|
||||
reveal_type(x) # revealed: T & Any
|
||||
reveal_type(x) # revealed: T@remove_constraint & Any
|
||||
```
|
||||
|
||||
The intersection of a typevar with any other type is assignable to (and if fully static, a subtype
|
||||
|
|
@ -710,7 +708,7 @@ A typevar bound to a Callable type is callable:
|
|||
from typing import Callable
|
||||
|
||||
def bound[T: Callable[[], int]](f: T):
|
||||
reveal_type(f) # revealed: T
|
||||
reveal_type(f) # revealed: T@bound
|
||||
reveal_type(f()) # revealed: int
|
||||
```
|
||||
|
||||
|
|
@ -718,7 +716,7 @@ Same with a constrained typevar, as long as all constraints are callable:
|
|||
|
||||
```py
|
||||
def constrained[T: (Callable[[], int], Callable[[], str])](f: T):
|
||||
reveal_type(f) # revealed: T
|
||||
reveal_type(f) # revealed: T@constrained
|
||||
reveal_type(f()) # revealed: int | str
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -152,9 +152,9 @@ already solved and specialized when the class was specialized:
|
|||
from ty_extensions import generic_context
|
||||
|
||||
legacy.m("string", None) # error: [invalid-argument-type]
|
||||
reveal_type(legacy.m) # revealed: bound method Legacy[int].m(x: int, y: S) -> S
|
||||
reveal_type(generic_context(Legacy)) # revealed: tuple[T]
|
||||
reveal_type(generic_context(legacy.m)) # revealed: tuple[S]
|
||||
reveal_type(legacy.m) # revealed: bound method Legacy[int].m(x: int, y: S@m) -> S@m
|
||||
reveal_type(generic_context(Legacy)) # revealed: tuple[T@Legacy]
|
||||
reveal_type(generic_context(legacy.m)) # revealed: tuple[S@m]
|
||||
```
|
||||
|
||||
With PEP 695 syntax, it is clearer that the method uses a separate typevar:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue