Passing a generic type via a parameter of type `type[T]` can leak an unsolved type variable
- Dominant language
- Python
- Stars
- 15.6k
- Forks
- 1.8k
- Avg merge
- 12h 13m
- Merged PRs (30d)
- 52
Description
**Describe the bug**
I have a generic class that allows specifying its type variable by passing a `type` object to the `__init__` method. The `__init__` method uses a function-scoped type variable for the passed-in `type` object as well as its `self` type. If the passed-in `type` object is itself a generic type, then Pyright seems to be leaking an unsolved type variable, even if the passed-in generic type has a default.
Context: The actual class wraps a NumPy array and allows you to pass in a NumPy dtype-like object in order to specify the array element type for an empty array. Its `__init__` method has an overload that takes `type[T] | np.dtype[T]` for the dtype-like object. The passed-in generic type is `np.bool`. This class turned up a issue with `np.bool` behavior in mypy: https://github.com/numpy/numpy/issues/29245 . While testing the workaround for that issue, I encountered this other issue with Pyright. @jorenham came up with a minimal reproduction that doesn't depend on NumPy.
**Code or Screenshots**
```
from typing import reveal_type
class Boolean[B: bool = bool]:
def __init__(self, b: B, /): ...
class Wrapper[T]:
dtype: type[T]
def __init__[U](self: Wrapper[U], dtype: type[U]) -> None:
self.dtype = dtype
reveal_type(Wrapper(Boolean)) # Wrapper[Boolean[B@Boolean]]
```
https://pyright-play.net/?pythonVersion=3.14&strict=true&code=GYJw9gtgBALgngBwJYDsDmUkQWEMogCmAboQIYA2A%2BvAoQFCMDGFZAzm1AEJhgXkoA2lwBcUAEa8KUALwSpAXRH0oqqABNCwKFSqokMXQAo2hCsAA0EsVysB6AJRiAdK%2BasOUAOogyCOiCCACpKKmrqtIRikcEKYaqa2rr6hlSCAKoKJmbAYj5%2BARkKVhGIUbBlRQ5QALQAfFAAcmAoUfFqqqbmzqV0shqRjESklDRlRvn%2BhCBGPHwCDg70QA
If you remove the type variable U and use T directly (`def __init__(self, dtype: type[T]) -> None:`), it doesn't leak an unsolved type variable:
```
Type of "Wrapper(Boolean)" is "Wrapper[Boolean[bool]]"
```
**VS Code extension or command-line**
pyright 1.1.402, command-line
Contributor guide
Research direction
Start by running the minimal reproduction from the issue with pyright 1.1.402 and compare the inferred type for Wrapper(Boolean) with the version that uses T directly. Trace the generic type inference for the function-scoped U and the defaulted Boolean type variable; done means the unsolved variable no longer leaks and the result is reported as Wrapper[Boolean[bool]].
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
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100