Union type incorrectly satisfies TypeVar bound to Protocol with Self in contravariant position
- Dominant language
- Python
- Stars
- 15.6k
- Forks
- 1.8k
- Avg merge
- 12h 13m
- Merged PRs (30d)
- 52
Description
**Describe the bug**
When a TypeVar is bound to a Protocol that uses Self as a parameter type (contravariant position), pyright incorrectly accepts a union type as satisfying the bound.
**Code or Screenshots**
```python
from typing import Protocol, Self, final
class SelfSerializable(Protocol):
@classmethod
def serialize(cls, value: Self) -> bytes: ...
@final
class Foo:
def __init__(self, x: int) -> None:
self.x = x
@classmethod
def serialize(cls, value: Self) -> bytes:
return value.x.to_bytes(4)
@final
class Bar:
def __init__(self, name: str) -> None:
self.name = name
@classmethod
def serialize(cls, value: Self) -> bytes:
return value.name.encode()
def serialize_other[T: SelfSerializable](template: T, value: T) -> bytes:
return type(template).serialize(value)
_ = serialize_other(Bar("name"), Foo(42))
```
0 diagnostics, runtime crash.
Writing `def serialize[T](cls: type[T], value: T) -> bytes: ...` in `SelfSerializable` makes no difference.
**VS Code extension or command-line**
1.1.408, through cmdline.
Contributor guide
Research direction
Start by reproducing the supplied Python example with the Pyright 1.1.408 command-line interface and confirm that it produces 0 diagnostics. Trace handling of the TypeVar bound, Protocol, Self in contravariant position, and union argument, then verify that the invalid serialize_other(Bar("name"), Foo(42)) call is diagnosed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100