mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 02:38:25 +00:00
[ty] Unify Type::is_subtype_of()
and Type::is_assignable_to()
(#18430)
Some checks are pending
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 / 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 / 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 / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
Some checks are pending
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 / 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 / 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 / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
This commit is contained in:
parent
1274521f9f
commit
6e785867c3
11 changed files with 394 additions and 588 deletions
|
@ -305,10 +305,13 @@ simplify to `Never`, even in the presence of other types:
|
|||
|
||||
```py
|
||||
from ty_extensions import Intersection, Not
|
||||
from typing import Any
|
||||
from typing import Any, Generic, TypeVar
|
||||
|
||||
T_co = TypeVar("T_co", covariant=True)
|
||||
|
||||
class P: ...
|
||||
class Q: ...
|
||||
class R(Generic[T_co]): ...
|
||||
|
||||
def _(
|
||||
i1: Intersection[P, Not[P]],
|
||||
|
@ -317,6 +320,8 @@ def _(
|
|||
i4: Intersection[Not[P], Q, P],
|
||||
i5: Intersection[P, Any, Not[P]],
|
||||
i6: Intersection[Not[P], Any, P],
|
||||
i7: Intersection[R[P], Not[R[P]]],
|
||||
i8: Intersection[R[P], Not[R[Q]]],
|
||||
) -> None:
|
||||
reveal_type(i1) # revealed: Never
|
||||
reveal_type(i2) # revealed: Never
|
||||
|
@ -324,6 +329,8 @@ def _(
|
|||
reveal_type(i4) # revealed: Never
|
||||
reveal_type(i5) # revealed: Never
|
||||
reveal_type(i6) # revealed: Never
|
||||
reveal_type(i7) # revealed: Never
|
||||
reveal_type(i8) # revealed: R[P] & ~R[Q]
|
||||
```
|
||||
|
||||
### Union of a type and its negation
|
||||
|
@ -332,20 +339,28 @@ Similarly, if we have both `P` and `~P` in a _union_, we can simplify that to `o
|
|||
|
||||
```py
|
||||
from ty_extensions import Intersection, Not
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
T_co = TypeVar("T_co", covariant=True)
|
||||
|
||||
class P: ...
|
||||
class Q: ...
|
||||
class R(Generic[T_co]): ...
|
||||
|
||||
def _(
|
||||
i1: P | Not[P],
|
||||
i2: Not[P] | P,
|
||||
i3: P | Q | Not[P],
|
||||
i4: Not[P] | Q | P,
|
||||
i5: R[P] | Not[R[P]],
|
||||
i6: R[P] | Not[R[Q]],
|
||||
) -> None:
|
||||
reveal_type(i1) # revealed: object
|
||||
reveal_type(i2) # revealed: object
|
||||
reveal_type(i3) # revealed: object
|
||||
reveal_type(i4) # revealed: object
|
||||
reveal_type(i5) # revealed: object
|
||||
reveal_type(i6) # revealed: R[P] | ~R[Q]
|
||||
```
|
||||
|
||||
### Negation is an involution
|
||||
|
|
|
@ -902,8 +902,7 @@ from ty_extensions import is_subtype_of, is_assignable_to, static_assert, TypeOf
|
|||
class HasX(Protocol):
|
||||
x: int
|
||||
|
||||
# TODO: this should pass
|
||||
static_assert(is_subtype_of(TypeOf[module], HasX)) # error: [static-assert-error]
|
||||
static_assert(is_subtype_of(TypeOf[module], HasX))
|
||||
static_assert(is_assignable_to(TypeOf[module], HasX))
|
||||
|
||||
class ExplicitProtocolSubtype(HasX, Protocol):
|
||||
|
|
|
@ -209,6 +209,34 @@ class AnyMeta(metaclass=Any): ...
|
|||
static_assert(is_assignable_to(type[AnyMeta], type))
|
||||
static_assert(is_assignable_to(type[AnyMeta], type[object]))
|
||||
static_assert(is_assignable_to(type[AnyMeta], type[Any]))
|
||||
|
||||
from typing import TypeVar, Generic, Any
|
||||
|
||||
T_co = TypeVar("T_co", covariant=True)
|
||||
|
||||
class Foo(Generic[T_co]): ...
|
||||
class Bar(Foo[T_co], Generic[T_co]): ...
|
||||
|
||||
static_assert(is_assignable_to(TypeOf[Bar[int]], type[Foo[int]]))
|
||||
static_assert(is_assignable_to(TypeOf[Bar[bool]], type[Foo[int]]))
|
||||
static_assert(is_assignable_to(TypeOf[Bar], type[Foo[int]]))
|
||||
static_assert(is_assignable_to(TypeOf[Bar[Any]], type[Foo[int]]))
|
||||
static_assert(is_assignable_to(TypeOf[Bar], type[Foo]))
|
||||
static_assert(is_assignable_to(TypeOf[Bar[Any]], type[Foo[Any]]))
|
||||
static_assert(is_assignable_to(TypeOf[Bar[Any]], type[Foo[int]]))
|
||||
|
||||
# TODO: these should pass (all subscripts inside `type[]` type expressions are currently TODO types)
|
||||
static_assert(not is_assignable_to(TypeOf[Bar[int]], type[Foo[bool]])) # error: [static-assert-error]
|
||||
static_assert(not is_assignable_to(TypeOf[Foo[bool]], type[Bar[int]])) # error: [static-assert-error]
|
||||
```
|
||||
|
||||
## `type[]` is not assignable to types disjoint from `builtins.type`
|
||||
|
||||
```py
|
||||
from typing import Any
|
||||
from ty_extensions import is_assignable_to, static_assert
|
||||
|
||||
static_assert(not is_assignable_to(type[Any], None))
|
||||
```
|
||||
|
||||
## Class-literals that inherit from `Any`
|
||||
|
@ -717,6 +745,53 @@ def f(x: int, y: str) -> None: ...
|
|||
c1: Callable[[int], None] = partial(f, y="a")
|
||||
```
|
||||
|
||||
### Generic classes with `__call__`
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.12"
|
||||
```
|
||||
|
||||
```py
|
||||
from typing_extensions import Callable, Any, Generic, TypeVar, ParamSpec
|
||||
from ty_extensions import static_assert, is_assignable_to
|
||||
|
||||
T = TypeVar("T")
|
||||
P = ParamSpec("P")
|
||||
|
||||
class Foo[T]:
|
||||
def __call__(self): ...
|
||||
|
||||
class FooLegacy(Generic[T]):
|
||||
def __call__(self): ...
|
||||
|
||||
class Bar[T, **P]:
|
||||
def __call__(self): ...
|
||||
|
||||
# TODO: should not error
|
||||
class BarLegacy(Generic[T, P]): # error: [invalid-argument-type] "`ParamSpec` is not a valid argument to `Generic`"
|
||||
def __call__(self): ...
|
||||
|
||||
static_assert(is_assignable_to(Foo, Callable[..., Any]))
|
||||
static_assert(is_assignable_to(FooLegacy, Callable[..., Any]))
|
||||
static_assert(is_assignable_to(Bar, Callable[..., Any]))
|
||||
static_assert(is_assignable_to(BarLegacy, Callable[..., Any]))
|
||||
|
||||
class Spam[T]: ...
|
||||
class SpamLegacy(Generic[T]): ...
|
||||
class Eggs[T, **P]: ...
|
||||
|
||||
# TODO: should not error
|
||||
class EggsLegacy(Generic[T, P]): ... # error: [invalid-argument-type] "`ParamSpec` is not a valid argument to `Generic`"
|
||||
|
||||
static_assert(not is_assignable_to(Spam, Callable[..., Any]))
|
||||
static_assert(not is_assignable_to(SpamLegacy, Callable[..., Any]))
|
||||
static_assert(not is_assignable_to(Eggs, Callable[..., Any]))
|
||||
|
||||
# TODO: should pass
|
||||
static_assert(not is_assignable_to(EggsLegacy, Callable[..., Any])) # error: [static-assert-error]
|
||||
```
|
||||
|
||||
### Classes with `__call__` as attribute
|
||||
|
||||
An instance type is assignable to a compatible callable type if the instance type's class has a
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue