microsoft / microsoft/pyright

Union type incorrectly satisfies TypeVar bound to Protocol with Self in contravariant position

Open
#11,374 0 comments 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.