microsoft / microsoft/pyright

A bunch of overlapping protocols in `@overload`s causes Pyright's runtime to explode.

Open
#11,048 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**
The following code causes Pyright to be stuck at 100% CPU usage for at least an hour. Where I manually cancelled the process.

The code is mid-rewrite, very broken, and likely makes no sense. But removing the final class causes 100% CPU usage for ~4s, so sorry for the massive code dump of half written nonsense.

**VS Code extension or command-line**
- VS Code with Pylance (new profile).
- The command-line tool (`pip install pyright`).

(Sorry not sure how to find the Pyright versions for both.)

Code

**WARNING**: You may want to use a VS Code profile without Pylance installed.

```python
from __future__ import annotations

import abc
from collections.abc import Iterable
import itertools
from typing import Any, Protocol, overload

__all__ = [
"IN_A",
"IN_D",
"IN_V",
"IN_AD",
"IN_AV",
"IN_DV",
"IN_ADV",

"IC_A",
"IC_D",
"IC_V",
"IC_AD",
"IC_AV",
"IC_DV",
"IC_ADV",
]

class IN_A[TConfig, TIn, TOut](metaclass=abc.ABCMeta):
pass

class IN_D[TConfig, TIn](metaclass=abc.ABCMeta):
pass

class IN_V[TConfig, TError, TIn](metaclass=abc.ABCMeta):
pass

class IN_AD[TConfig, TIn, TOut](IN_A[TConfig, TIn, TOut], IN_D[TConfig, TIn], metaclass=abc.ABCMeta):
pass

class IN_AV[TConfig, TError, TIn, TOut](IN_A[TConfig, TIn, TOut], IN_V[TConfig, TError, TIn], metaclass=abc.ABCMeta):
pass

class IN_DV[TConfig, TError, TIn](IN_D[TConfig, TIn], IN_V[TConfig, TError, TIn], metaclass=abc.ABCMeta):
pass

class IN_ADV[TConfig, TError, TIn, TOut](IN_A[TConfig, TIn, TOut], IN_D[TConfig, TIn], IN_V[TConfig, TError, TIn], metaclass=abc.ABCMeta):
pass

class IC_A[TConfig, TIn, TOut](IN_A[TConfig, TIn, TOut], Protocol, metaclass=abc.ABCMeta):
@overload
def chain[TC, TE, TO](self, other: IN_ADV[TC, TE, TOut, TO]) -> IC_ADV[TConfig | TC, TE, TIn, TO]: ...

@overload
def chain[TC, TO](self, other: IN_AD[TC, TOut, TO]) -> IC_AD[TConfig | TC, TIn, TO]: ...

@overload
def chain[TC, TE, TO](self, other: IN_AV[TC, TE, TOut, TO]) -> IC_AV[TConfig | TC, TE, TIn, TO]: ...

@overload
def chain[TC, TE](self, other: IN_DV[TC, TE, TOut]) -> IC_ADV[TConfig | TC, TE, TIn]: ...

@overload
def chain[TC, TO](self, other: IN_A[TC, TOut, TO]) -> IC_A[TConfig | TC, TIn, TO]: ...

@overload
def chain[TC](self, other: IN_D[TC, TOut]) -> IC_AD[TConfig | TC, TIn]: ...

@overload
def chain[TC, TE](self, other: IN_V[TC, TE, TOut]) -> IC_AV[TConfig | TC, TE, TIn]: ...

@overload
def chain(self, other: Any) -> Any: # pyright: ignore[reportExplicitAny, reportAny]
raise NotImplementedError(f"{type(self).__name__}.chain has not been defined")

class IC_D[TConfig, TIn](IN_D[TConfig, TIn], Protocol, metaclass=abc.ABCMeta):
@overload
def chain[TC, TE, TO](self, other: IN_ADV[TC, TE, TIn, TO]) -> IC_ADV[TConfig | TC, TE, TIn, TO]: ...

@overload
def chain[TC, TO](self, other: IN_AD[TC, TIn, TO]) -> IC_AD[TConfig | TC, TIn, TO]: ...

@overload
def chain[TC, TE, TO](self, other: IN_AV[TC, TE, TIn, TO]) -> IC_ADV[TConfig | TC, TE, TIn, TO]: ...

@overload
def chain[TC, TE](self, other: IN_DV[TC, TE, TIn]) -> IC_DV[TConfig | TC, TE, TIn]: ...

@overload
def chain[TC, TO](self, other: IN_A[TC, TIn, TO]) -> IC_AD[TConfig | TC, TIn, TO]: ...

@overload
def chain[TC](self, other: IN_D[TC, TIn]) -> IC_D[TConfig | TC, TIn]: ...

@overload
def chain[TC, TE](self, other: IN_V[TC, TE, TIn]) -> IC_DV[TConfig | TC, TE, TIn]: ...

@overload
def chain(self, other: Any) -> Any: # pyright: ignore[reportExplicitAny, reportAny]
raise NotImplementedError(f"{type(self).__name__}.chain has not been defined")

class IC_V[TConfig, TError, TIn](IN_V[TConfig, TError, TIn], Protocol, metaclass=abc.ABCMeta):
@overload
def chain[TC, TE, TO](self, other: IN_ADV[TC, TE, TIn, TO]) -> IC_ADV[TConfig | TC, TError | TE, TIn, TO]: ...

@overload
def chain[TC, TO](self, other: IN_AD[TC, TIn, TO]) -> IC_ADV[TConfig | TC, TError, TIn, TO]: ...

@overload
def chain[TC, TE, TO](self, other: IN_AV[TC, TE, TIn, TO]) -> IC_AV[TConfig | TC, TError | TE, TIn, TO]: ...

@overload
def chain[TC, TE](self, other: IN_DV[TC, TE, TIn]) -> IC_DV[TConfig | TC, TError | TE, TIn]: ...

@overload
def chain[TC, TO](self, other: IN_A[TC, TIn, TO]) -> IC_AV[TConfig | TC, TError, TIn, TO]: ...

@overload
def chain[TC](self, other: IN_D[TC, TIn]) -> IC_DV[TConfig | TC, TError, TIn]: ...

@overload
def chain[TC, TE](self, other: IN_V[TC, TE, TIn]) -> IC_V[TConfig | TC, TError | TE, TIn]: ...

@overload
def chain(self, other: Any) -> Any: # pyright: ignore[reportExplicitAny, reportAny]
raise NotImplementedError(f"{type(self).__name__}.chain has not been defined")

class IC_AD[TConfig, TIn, TOut](IN_AD[TConfig, TIn, TOut], Protocol, metaclass=abc.ABCMeta):
@overload
def chain[TC, TE, TO](self, other: IN_ADV[TC, TE, TOut, TO]) -> IC_ADV[TConfig | TC, TE, TIn, TO]: ...

@overload
def chain[TC, TO](self, other: IN_AD[TC, TOut, TO]) -> IC_AD[TConfig | TC, TIn, TO]: ...

@overload
def chain[TC, TE, TO](self, other: IN_AV[TC, TE, TOut, TO]) -> IC_ADV[TConfig | TC, TE, TIn, TO]: ...

@overload
def chain[TC, TE](self, other: IN_DV[TC, TE, TOut]) -> IC_ADV[TConfig | TC, TE, TIn]: ...

@overload
def chain[TC, TO](self, other: IN_A[TC, TOut, TO]) -> IC_AD[TConfig | TC, TIn, TO]: ...

@overload
def chain[TC](self, other: IN_D[TC, TOut]) -> IC_AD[TConfig | TC, TIn]: ...

@overload
def chain[TC, TE](self, other: IN_V[TC, TE, TOut]) -> IC_ADV[TConfig | TC, TE, TIn]: ...

@overload
def chain(self, other: Any) -> Any: # pyright: ignore[reportExplicitAny, reportAny]
raise NotImplementedError(f"{type(self).__name__}.chain has not been defined")

class IC_AV[TConfig, TError, TIn, TOut](IN_AV[TConfig, TError, TIn, TOut], Protocol, metaclass=abc.ABCMeta):
@overload
def chain[TC, TE, TO](self, other: IN_ADV[TC, TE, TOut, TO]) -> IC_ADV[TConfig | TC, TError | TE, TIn, TO]: ...

@overload
def chain[TC, TO](self, other: IN_AD[TC, TOut, TO]) -> IC_ADV[TConfig | TC, TError, TIn, TO]: ...

@overload
def chain[TC, TE, TO](self, other: IN_AV[TC, TE, TOut, TO]) -> IC_AV[TConfig | TC, TError | TE, TIn, TO]: ...

@overload
def chain[TC, TE](self, other: IN_DV[TC, TE, TOut]) -> IC_ADV[TConfig | TC, TError | TE, TIn]: ...

@overload
def chain[TC, TO](self, other: IN_A[TC, TOut, TO]) -> IC_AV[TConfig | TC, TError, TIn, TO]: ...

@overload
def chain[TC](self, other: IN_D[TC, TOut]) -> IC_ADV[TConfig | TC, TError, TIn]: ...

@overload
def chain[TC, TE](self, other: IN_V[TC, TE, TOut]) -> IC_AV[TConfig | TC, TError | TE, TIn]: ...

@overload
def chain(self, other: Any) -> Any: # pyright: ignore[reportExplicitAny, reportAny]
raise NotImplementedError(f"{type(self).__name__}.chain has not been defined")

class IC_DV[TConfig, TError, TIn](IN_DV[TConfig, TError, TIn], Protocol, metaclass=abc.ABCMeta):
@overload
def chain[TC, TE, TO](self, other: IN_ADV[TC, TE, TIn, TO]) -> IC_ADV[TConfig | TC, TError | TE, TIn, TO]: ...

@overload
def chain[TC, TO](self, other: IN_AD[TC, TIn, TO]) -> IC_ADV[TConfig | TC, TError, TIn, TO]: ...

@overload
def chain[TC, TE, TO](self, other: IN_AV[TC, TE, TIn, TO]) -> IC_ADV[TConfig | TC, TError | TE, TIn, TO]: ...

@overload
def chain[TC, TE](self, other: IN_DV[TC, TE, TIn]) -> IC_DV[TConfig | TC, TError | TE, TIn]: ...

@overload
def chain[TC, TO](self, other: IN_A[TC, TIn, TO]) -> IC_ADV[TConfig | TC, TError, TIn, TO]: ...

@overload
def chain[TC](self, other: IN_D[TC, TIn]) -> IC_DV[TConfig | TC, TError, TIn]: ...

@overload
def chain[TC, TE](self, other: IN_V[TC, TE, TIn]) -> IC_DV[TConfig | TC, TError | TE, TIn]: ...

@overload
def chain(self, other: Any) -> Any: # pyright: ignore[reportExplicitAny, reportAny]
raise NotImplementedError(f"{type(self).__name__}.chain has not been defined")

class IC_ADV[TConfig, TError, TIn, TOut](IN_ADV[TConfig, TError, TIn, TOut], Protocol, metaclass=abc.ABCMeta):
@overload
def chain[TC, TE, TO](self, other: IN_ADV[TC, TE, TOut, TO]) -> IC_ADV[TConfig | TC, TError | TE, TIn, TO]: ...

@overload
def chain[TC, TO](self, other: IN_AD[TC, TOut, TO]) -> IC_ADV[TConfig | TC, TError, TIn, TO]: ...

@overload
def chain[TC, TE, TO](self, other: IN_AV[TC, TE, TOut, TO]) -> IC_ADV[TConfig | TC, TError | TE, TIn, TO]: ...

@overload
def chain[TC, TE](self, other: IN_DV[TC, TE, TOut]) -> IC_ADV[TConfig | TC, TError | TE, TIn]: ...

@overload
def chain[TC, TO](self, other: IN_A[TC, TOut, TO]) -> IC_ADV[TConfig | TC, TError, TIn, TO]: ...

@overload
def chain[TC](self, other: IN_D[TC, TOut]) -> IC_ADV[TConfig | TC, TError, TIn]: ...

@overload
def chain[TC, TE](self, other: IN_V[TC, TE, TOut]) -> IC_ADV[TConfig | TC, TError | TE, TIn]: ...

@overload
def chain(self, other: Any) -> Any: # pyright: ignore[reportExplicitAny, reportAny]
raise NotImplementedError(f"{type(self).__name__}.chain has not been defined")
```

Contributor guide

Open the contributing guide

Research direction

Run the supplied Python reproducer with the command-line tool and compare its behavior with VS Code/Pylance. Trace the type-checking path for the overlapping Protocol and @overload declarations; done means analysis completes without sustained 100% CPU usage.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.