Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

how to make protocol compatible with bounded TypeVar #1865

Answered by Daverball
ViktorSky asked this question in Q&A
Discussion options

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]
You must be logged in to vote

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

Comment options

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]
You must be logged in to vote
1 reply
Comment options

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]
Answer selected by ViktorSky
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /