[ty] Substitute for typing.Self when checking protocol members (#21569)
This patch updates our protocol assignability checks to substitute for any occurrences of `typing.Self` in method signatures, replacing it with the class being checked for assignability against the protocol. This requires a new helper method on signatures, `apply_self`, which substitutes occurrences of `typing.Self` _without_ binding the `self` parameter. We also update the `try_upcast_to_callable` method. Before, it would return a `Type`, since certain types upcast to a _union_ of callables, not to a single callable. However, even in that case, we know that every element of the union is a callable. We now return a vector of `CallableType`. (Actually a smallvec to handle the most common case of a single callable; and wrapped in a new type so that we can provide helper methods.) If there is more than one element in the result, it represents a union of callables. This lets callers get at the `CallableType` instances in a more type-safe way. (This makes it easier for our protocol checking code to call the new `apply_self` helper.) We also provide an `into_type` method so that callers that really do want a `Type` can get the original result easily. --------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
@@ -2003,6 +2003,7 @@ python-version = "3.12"
|
||||
```
|
||||
|
||||
```py
|
||||
from typing import final
|
||||
from typing_extensions import TypeVar, Self, Protocol
|
||||
from ty_extensions import is_equivalent_to, static_assert, is_assignable_to, is_subtype_of
|
||||
|
||||
@@ -2094,6 +2095,13 @@ class NominalReturningSelfNotGeneric:
|
||||
def g(self) -> "NominalReturningSelfNotGeneric":
|
||||
return self
|
||||
|
||||
@final
|
||||
class Other: ...
|
||||
|
||||
class NominalReturningOtherClass:
|
||||
def g(self) -> Other:
|
||||
raise NotImplementedError
|
||||
|
||||
# TODO: should pass
|
||||
static_assert(is_equivalent_to(LegacyFunctionScoped, NewStyleFunctionScoped)) # error: [static-assert-error]
|
||||
|
||||
@@ -2112,8 +2120,7 @@ static_assert(not is_assignable_to(NominalLegacy, UsesSelf))
|
||||
static_assert(not is_assignable_to(NominalWithSelf, NewStyleFunctionScoped))
|
||||
static_assert(not is_assignable_to(NominalWithSelf, LegacyFunctionScoped))
|
||||
static_assert(is_assignable_to(NominalWithSelf, UsesSelf))
|
||||
# TODO: should pass
|
||||
static_assert(is_subtype_of(NominalWithSelf, UsesSelf)) # error: [static-assert-error]
|
||||
static_assert(is_subtype_of(NominalWithSelf, UsesSelf))
|
||||
|
||||
# TODO: these should pass
|
||||
static_assert(not is_assignable_to(NominalNotGeneric, NewStyleFunctionScoped)) # error: [static-assert-error]
|
||||
@@ -2126,6 +2133,8 @@ static_assert(not is_assignable_to(NominalReturningSelfNotGeneric, LegacyFunctio
|
||||
# TODO: should pass
|
||||
static_assert(not is_assignable_to(NominalReturningSelfNotGeneric, UsesSelf)) # error: [static-assert-error]
|
||||
|
||||
static_assert(not is_assignable_to(NominalReturningOtherClass, UsesSelf))
|
||||
|
||||
# These test cases are taken from the typing conformance suite:
|
||||
class ShapeProtocolImplicitSelf(Protocol):
|
||||
def set_scale(self, scale: float) -> Self: ...
|
||||
|
||||
Reference in New Issue
Block a user