mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 05:14:52 +00:00
[ty] Make TypeIs invariant in its type argument (#20428)
## Summary What it says on the tin. See the [typing spec](https://docs.python.org/3/library/typing.html#typing.TypeIs) for justification. ## Test Plan Add more tests to PEP 695 `variance.md` suite.
This commit is contained in:
parent
144373fb3c
commit
2502ff7638
2 changed files with 42 additions and 1 deletions
|
@ -752,6 +752,36 @@ b_container = ClassContainer[B](B)
|
||||||
a_instance: A = use_a_class_container(b_container) # This should work
|
a_instance: A = use_a_class_container(b_container) # This should work
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## TypeIs
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[environment]
|
||||||
|
python-version = "3.13"
|
||||||
|
```
|
||||||
|
|
||||||
|
`TypeIs[T]` is invariant in `T`. See the [typing spec][typeis-spec] for a justification.
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import TypeIs
|
||||||
|
from ty_extensions import is_assignable_to, is_subtype_of, static_assert
|
||||||
|
|
||||||
|
class A:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class B(A):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class C[T]:
|
||||||
|
def check(x: object) -> TypeIs[T]:
|
||||||
|
# this is a bad check, but we only care about it type-checking
|
||||||
|
return False
|
||||||
|
|
||||||
|
static_assert(not is_subtype_of(C[B], C[A]))
|
||||||
|
static_assert(not is_subtype_of(C[A], C[B]))
|
||||||
|
static_assert(not is_assignable_to(C[B], C[A]))
|
||||||
|
static_assert(not is_assignable_to(C[A], C[B]))
|
||||||
|
```
|
||||||
|
|
||||||
## Inheriting from generic classes with inferred variance
|
## Inheriting from generic classes with inferred variance
|
||||||
|
|
||||||
When inheriting from a generic class with our type variable substituted in, we count its occurrences
|
When inheriting from a generic class with our type variable substituted in, we count its occurrences
|
||||||
|
@ -837,3 +867,4 @@ static_assert(is_subtype_of(DerivedContravariant[A], DerivedContravariant[B]))
|
||||||
|
|
||||||
[linear-time-variance-talk]: https://www.youtube.com/watch?v=7uixlNTOY4s&t=9705s
|
[linear-time-variance-talk]: https://www.youtube.com/watch?v=7uixlNTOY4s&t=9705s
|
||||||
[spec]: https://typing.python.org/en/latest/spec/generics.html#variance
|
[spec]: https://typing.python.org/en/latest/spec/generics.html#variance
|
||||||
|
[typeis-spec]: https://typing.python.org/en/latest/spec/narrowing.html#typeis
|
||||||
|
|
|
@ -6620,6 +6620,7 @@ impl<'db> VarianceInferable<'db> for Type<'db> {
|
||||||
.map(|ty| ty.variance_of(db, typevar))
|
.map(|ty| ty.variance_of(db, typevar))
|
||||||
.collect(),
|
.collect(),
|
||||||
Type::SubclassOf(subclass_of_type) => subclass_of_type.variance_of(db, typevar),
|
Type::SubclassOf(subclass_of_type) => subclass_of_type.variance_of(db, typevar),
|
||||||
|
Type::TypeIs(type_is_type) => type_is_type.variance_of(db, typevar),
|
||||||
Type::Dynamic(_)
|
Type::Dynamic(_)
|
||||||
| Type::Never
|
| Type::Never
|
||||||
| Type::WrapperDescriptor(_)
|
| Type::WrapperDescriptor(_)
|
||||||
|
@ -6640,7 +6641,6 @@ impl<'db> VarianceInferable<'db> for Type<'db> {
|
||||||
| Type::BoundSuper(_)
|
| Type::BoundSuper(_)
|
||||||
| Type::TypeVar(_)
|
| Type::TypeVar(_)
|
||||||
| Type::NonInferableTypeVar(_)
|
| Type::NonInferableTypeVar(_)
|
||||||
| Type::TypeIs(_)
|
|
||||||
| Type::TypedDict(_)
|
| Type::TypedDict(_)
|
||||||
| Type::TypeAlias(_) => TypeVarVariance::Bivariant,
|
| Type::TypeAlias(_) => TypeVarVariance::Bivariant,
|
||||||
};
|
};
|
||||||
|
@ -10956,6 +10956,16 @@ impl<'db> TypeIsType<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'db> VarianceInferable<'db> for TypeIsType<'db> {
|
||||||
|
// See the [typing spec] on why `TypeIs` is invariant in its type.
|
||||||
|
// [typing spec]: https://typing.python.org/en/latest/spec/narrowing.html#typeis
|
||||||
|
fn variance_of(self, db: &'db dyn Db, typevar: BoundTypeVarInstance<'db>) -> TypeVarVariance {
|
||||||
|
self.return_type(db)
|
||||||
|
.with_polarity(TypeVarVariance::Invariant)
|
||||||
|
.variance_of(db, typevar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Walk the MRO of this class and return the last class just before the specified known base.
|
/// Walk the MRO of this class and return the last class just before the specified known base.
|
||||||
/// This can be used to determine upper bounds for `Self` type variables on methods that are
|
/// This can be used to determine upper bounds for `Self` type variables on methods that are
|
||||||
/// being added to the given class.
|
/// being added to the given class.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue