mirror of https://github.com/astral-sh/ruff
add failing tests
This commit is contained in:
parent
8623176dc5
commit
671923bb6b
|
|
@ -341,4 +341,93 @@ def mutually_constrained[T, U]():
|
|||
static_assert(not given_int.implies_subtype_of(Invariant[T], Invariant[str]))
|
||||
```
|
||||
|
||||
## Generic callables
|
||||
|
||||
A generic callable can be considered equivalent to an intersection of all of its possible
|
||||
specializations. That means that a generic callable is a subtype of any particular specialization.
|
||||
(If someone expects a function that works with a particular specialization, it's fine to hand them
|
||||
the generic callable.)
|
||||
|
||||
```py
|
||||
from typing import Callable
|
||||
from ty_extensions import CallableTypeOf, ConstraintSet, TypeOf, is_subtype_of, static_assert
|
||||
|
||||
def identity[T](t: T) -> T:
|
||||
return t
|
||||
|
||||
constraints = ConstraintSet.always()
|
||||
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(constraints.implies_subtype_of(TypeOf[identity], Callable[[int], int]))
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(constraints.implies_subtype_of(TypeOf[identity], Callable[[str], str]))
|
||||
static_assert(not constraints.implies_subtype_of(TypeOf[identity], Callable[[str], int]))
|
||||
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(constraints.implies_subtype_of(CallableTypeOf[identity], Callable[[int], int]))
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(constraints.implies_subtype_of(CallableTypeOf[identity], Callable[[str], str]))
|
||||
static_assert(not constraints.implies_subtype_of(CallableTypeOf[identity], Callable[[str], int]))
|
||||
```
|
||||
|
||||
The reverse is not true — if someone expects a generic function that can be called with any
|
||||
specialization, we cannot hand them a function that only works with one specialization.
|
||||
|
||||
```py
|
||||
static_assert(not constraints.implies_subtype_of(Callable[[int], int], TypeOf[identity]))
|
||||
static_assert(not constraints.implies_subtype_of(Callable[[str], str], TypeOf[identity]))
|
||||
static_assert(not constraints.implies_subtype_of(Callable[[str], int], TypeOf[identity]))
|
||||
|
||||
static_assert(not constraints.implies_subtype_of(Callable[[int], int], CallableTypeOf[identity]))
|
||||
static_assert(not constraints.implies_subtype_of(Callable[[str], str], CallableTypeOf[identity]))
|
||||
static_assert(not constraints.implies_subtype_of(Callable[[str], int], CallableTypeOf[identity]))
|
||||
```
|
||||
|
||||
Unrelated typevars in the constraint set do not affect whether the subtyping check succeeds or
|
||||
fails.
|
||||
|
||||
```py
|
||||
def unrelated[T]():
|
||||
# Note that even though this typevar is also named T, it is not the same typevar as T@identity!
|
||||
constraints = ConstraintSet.range(bool, T, int)
|
||||
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(constraints.implies_subtype_of(TypeOf[identity], Callable[[int], int]))
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(constraints.implies_subtype_of(TypeOf[identity], Callable[[str], str]))
|
||||
static_assert(not constraints.implies_subtype_of(TypeOf[identity], Callable[[str], int]))
|
||||
|
||||
static_assert(not constraints.implies_subtype_of(Callable[[int], int], TypeOf[identity]))
|
||||
static_assert(not constraints.implies_subtype_of(Callable[[str], str], TypeOf[identity]))
|
||||
static_assert(not constraints.implies_subtype_of(Callable[[str], int], TypeOf[identity]))
|
||||
```
|
||||
|
||||
The generic callable's typevar _also_ does not affect whether the subtyping check succeeds or fails!
|
||||
|
||||
```py
|
||||
def identity2[T](t: T) -> T:
|
||||
# This constraint set refers to the same typevar as the generic function types below!
|
||||
constraints = ConstraintSet.range(bool, T, int)
|
||||
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(constraints.implies_subtype_of(TypeOf[identity2], Callable[[int], int]))
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(constraints.implies_subtype_of(TypeOf[identity2], Callable[[str], str]))
|
||||
static_assert(not constraints.implies_subtype_of(TypeOf[identity2], Callable[[str], int]))
|
||||
|
||||
static_assert(not constraints.implies_subtype_of(Callable[[int], int], TypeOf[identity2]))
|
||||
static_assert(not constraints.implies_subtype_of(Callable[[str], str], TypeOf[identity2]))
|
||||
static_assert(not constraints.implies_subtype_of(Callable[[str], int], TypeOf[identity2]))
|
||||
|
||||
return t
|
||||
```
|
||||
|
||||
[subtyping]: https://typing.python.org/en/latest/spec/concepts.html#subtype-supertype-and-type-equivalence
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
# Assignable-to relation
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.12"
|
||||
```
|
||||
|
||||
The `is_assignable_to(S, T)` relation below checks if type `S` is assignable to type `T` (target).
|
||||
This allows us to check if a type `S` can be used in a context where a type `T` is expected
|
||||
(function arguments, variable assignments). See the [typing documentation] for a precise definition
|
||||
|
|
@ -1227,6 +1232,46 @@ from ty_extensions import static_assert, is_assignable_to
|
|||
static_assert(is_assignable_to(type, Callable[..., Any]))
|
||||
```
|
||||
|
||||
### Generic callables
|
||||
|
||||
A generic callable can be considered equivalent to an intersection of all of its possible
|
||||
specializations. That means that a generic callable is assignable to any particular specialization.
|
||||
(If someone expects a function that works with a particular specialization, it's fine to hand them
|
||||
the generic callable.)
|
||||
|
||||
```py
|
||||
from typing import Callable
|
||||
from ty_extensions import CallableTypeOf, TypeOf, is_assignable_to, static_assert
|
||||
|
||||
def identity[T](t: T) -> T:
|
||||
return t
|
||||
|
||||
static_assert(is_assignable_to(TypeOf[identity], Callable[[int], int]))
|
||||
static_assert(is_assignable_to(TypeOf[identity], Callable[[str], str]))
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(not is_assignable_to(TypeOf[identity], Callable[[str], int]))
|
||||
|
||||
static_assert(is_assignable_to(CallableTypeOf[identity], Callable[[int], int]))
|
||||
static_assert(is_assignable_to(CallableTypeOf[identity], Callable[[str], str]))
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(not is_assignable_to(CallableTypeOf[identity], Callable[[str], int]))
|
||||
```
|
||||
|
||||
The reverse is not true — if someone expects a generic function that can be called with any
|
||||
specialization, we cannot hand them a function that only works with one specialization.
|
||||
|
||||
```py
|
||||
static_assert(not is_assignable_to(Callable[[int], int], TypeOf[identity]))
|
||||
static_assert(not is_assignable_to(Callable[[str], str], TypeOf[identity]))
|
||||
static_assert(not is_assignable_to(Callable[[str], int], TypeOf[identity]))
|
||||
|
||||
static_assert(not is_assignable_to(Callable[[int], int], CallableTypeOf[identity]))
|
||||
static_assert(not is_assignable_to(Callable[[str], str], CallableTypeOf[identity]))
|
||||
static_assert(not is_assignable_to(Callable[[str], int], CallableTypeOf[identity]))
|
||||
```
|
||||
|
||||
## Generics
|
||||
|
||||
### Assignability of generic types parameterized by gradual types
|
||||
|
|
|
|||
|
|
@ -2207,6 +2207,50 @@ static_assert(is_subtype_of(CallableTypeOf[overload_ab], CallableTypeOf[overload
|
|||
static_assert(is_subtype_of(CallableTypeOf[overload_ba], CallableTypeOf[overload_ab]))
|
||||
```
|
||||
|
||||
### Generic callables
|
||||
|
||||
A generic callable can be considered equivalent to an intersection of all of its possible
|
||||
specializations. That means that a generic callable is a subtype of any particular specialization.
|
||||
(If someone expects a function that works with a particular specialization, it's fine to hand them
|
||||
the generic callable.)
|
||||
|
||||
```py
|
||||
from typing import Callable
|
||||
from ty_extensions import CallableTypeOf, TypeOf, is_subtype_of, static_assert
|
||||
|
||||
def identity[T](t: T) -> T:
|
||||
return t
|
||||
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(is_subtype_of(TypeOf[identity], Callable[[int], int]))
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(is_subtype_of(TypeOf[identity], Callable[[str], str]))
|
||||
static_assert(not is_subtype_of(TypeOf[identity], Callable[[str], int]))
|
||||
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(is_subtype_of(CallableTypeOf[identity], Callable[[int], int]))
|
||||
# TODO: no error
|
||||
# error: [static-assert-error]
|
||||
static_assert(is_subtype_of(CallableTypeOf[identity], Callable[[str], str]))
|
||||
static_assert(not is_subtype_of(CallableTypeOf[identity], Callable[[str], int]))
|
||||
```
|
||||
|
||||
The reverse is not true — if someone expects a generic function that can be called with any
|
||||
specialization, we cannot hand them a function that only works with one specialization.
|
||||
|
||||
```py
|
||||
static_assert(not is_subtype_of(Callable[[int], int], TypeOf[identity]))
|
||||
static_assert(not is_subtype_of(Callable[[str], str], TypeOf[identity]))
|
||||
static_assert(not is_subtype_of(Callable[[str], int], TypeOf[identity]))
|
||||
|
||||
static_assert(not is_subtype_of(Callable[[int], int], CallableTypeOf[identity]))
|
||||
static_assert(not is_subtype_of(Callable[[str], str], CallableTypeOf[identity]))
|
||||
static_assert(not is_subtype_of(Callable[[str], int], CallableTypeOf[identity]))
|
||||
```
|
||||
|
||||
[gradual form]: https://typing.python.org/en/latest/spec/glossary.html#term-gradual-form
|
||||
[gradual tuple]: https://typing.python.org/en/latest/spec/tuples.html#tuple-type-form
|
||||
[special case for float and complex]: https://typing.python.org/en/latest/spec/special-types.html#special-cases-for-float-and-complex
|
||||
|
|
|
|||
Loading…
Reference in New Issue