-
Notifications
You must be signed in to change notification settings - Fork 288
-
While creating stubs files, I ran into this problem
In this code (example), I need to make "SupportsPlaceholderValue" usable as a type argument to specify the return type of result
To recreate the expected solution ignore the error of subclassing a protocol from a non-protocol class
import typing T = typing.TypeVar("T") T_co = typing.TypeVar("T_co", covariant=True) TraceTypeT = typing.TypeVar("TraceTypeT", bound="TraceType") class TraceType: # no editable def from_tensors(self, tensors: list[typing.Any]) -> typing.Any: ... def placeholder_value(self) -> typing.Any: ... # Protocol class "SupportsPlaceholderValue" cannot derive from non-Protocol class "TraceType" class SupportsPlaceholderValue(TraceType, typing.Protocol[T_co]): # <-- error def placeholder_value(self) -> T_co: ... class MyTraceType(typing.Generic[TraceTypeT], TraceType): def __init__(self, *components: TraceTypeT) -> None: self.components: tuple[TraceTypeT, ...] # error: SupportsPlaceholderValue[T@result]" is not assignable to "TraceType" def result(self: MyTraceType[SupportsPlaceholderValue[T]]) -> list[T]: # ??? # .py implementation return [c.placeholder_value() for c in self.components]
Beta Was this translation helpful? Give feedback.
All reactions
Alternatively you can create a Protocol for the whole TraceType, if you don't actually care that it's an actual instance of TraceType, but rather just has the correct shape:
from __future__ import annotations
import typing
T = typing.TypeVar("T")
T_co = typing.TypeVar("T_co", covariant=True)
TraceLikeT = typing.TypeVar("TraceLikeT", bound="TraceLike[typing.Any]")
class TraceType: # no editable
def from_tensors(self, tensors: list[typing.Any]) -> typing.Any: ...
def placeholder_value(self) -> typing.Any: ...
class TraceLike(typing.Protocol[T_co]):
def from_tensors(self, tensors: list[typing.Any]) -> typing.Any: ...
def placeholder_value(self) -> T_co: ...
class MyTrace...Replies: 1 comment 1 reply
-
TraceType is a nominal type, so you can't mix it with a structural type like Protocol, you would need type intersections, which aren't a thing yet. But even with type intersections you would end up with T_co & Any rather than T_co for your elements, so it wouldn't really accomplish what you're trying to do.
The best you can do is provide a generic TraceType that people can subclass and pass into MyTraceType and then add an overload to result so these generic trace types can give a more accurate return type, so something like this:
from __future__ import annotations import typing T = typing.TypeVar("T") T_co = typing.TypeVar("T_co", covariant=True) TraceTypeT = typing.TypeVar("TraceTypeT", bound="TraceType") class TraceType: # no editable def from_tensors(self, tensors: list[typing.Any]) -> typing.Any: ... def placeholder_value(self) -> typing.Any: ... class GenericTraceType(TraceType, typing.Generic[T_co]): if typing.TYPE_CHECKING: def placeholder_value(self) -> T_co: ... class MyTraceType(typing.Generic[TraceTypeT], TraceType): def __init__(self, *components: TraceTypeT) -> None: self.components: tuple[TraceTypeT, ...] @typing.overload def result(self: MyTraceType[GenericTraceType[T]]) -> list[T]: ... @typing.overload def result(self) -> list[typing.Any]: ... def result(self) -> list[typing.Any]: # .py implementation return [c.placeholder_value() for c in self.components]
Beta Was this translation helpful? Give feedback.
All reactions
-
Alternatively you can create a Protocol for the whole TraceType, if you don't actually care that it's an actual instance of TraceType, but rather just has the correct shape:
from __future__ import annotations
import typing
T = typing.TypeVar("T")
T_co = typing.TypeVar("T_co", covariant=True)
TraceLikeT = typing.TypeVar("TraceLikeT", bound="TraceLike[typing.Any]")
class TraceType: # no editable
def from_tensors(self, tensors: list[typing.Any]) -> typing.Any: ...
def placeholder_value(self) -> typing.Any: ...
class TraceLike(typing.Protocol[T_co]):
def from_tensors(self, tensors: list[typing.Any]) -> typing.Any: ...
def placeholder_value(self) -> T_co: ...
class MyTraceType(typing.Generic[TraceLikeT], TraceType):
def __init__(self, *components: TraceLikeT) -> None:
self.components: tuple[TraceLikeT, ...]
def result(self: MyTraceType[TraceLike[T]]) -> list[T]:
# .py implementation
return [c.placeholder_value() for c in self.components]
Beta Was this translation helpful? Give feedback.