Referring to a different instantiation whilst defining a generic class
- Dominant language
- Python
- Stars
- 15.6k
- Forks
- 1.8k
- Avg merge
- 12h 13m
- Merged PRs (30d)
- 52
Description
The following generic generic class and a function to construct it type-checks fine:
```py
from __future__ import annotations
from typing import TypeVar, Generic
T = TypeVar("T")
S = TypeVar("S")
class C(Generic[T]):
pass
def standalone(_dummy: S | None = None) -> C[S]:
ty: type[C[S]] = C
return object.__new__(ty)
```
But trying to move `standalone` into `C` as any kind of method breaks it:
```py
from __future__ import annotations
from typing import TypeVar, Generic
T = TypeVar("T")
S = TypeVar("S")
class C(Generic[T]):
def meth(self, _dummy: S | None = None) -> C[S]:
ty: type[C[S]] = C
return object.__new__(ty)
@classmethod
def classmeth(cls, _dummy: S | None = None) -> C[S]:
ty: type[C[S]] = C
return object.__new__(ty)
@staticmethod
def staticmeth(_dummy: S | None = None) -> C[S]:
ty: type[C[S]] = C
return object.__new__(ty)
```
```
test.py
test.py:8:26 - error: Type "type[C[T@C]]" is not assignable to declared type "type[C[S@meth]]"
"type[C[T@C]]" is not assignable to "type[C[S@meth]]"
Type "type[C[T@C]]" is not assignable to type "type[C[S@meth]]"
Type parameter "T@C" is invariant, but "T@C" is not the same as "S@meth" (reportAssignmentType)
test.py:13:26 - error: Type "type[C[T@C]]" is not assignable to declared type "type[C[S@classmeth]]"
"type[C[T@C]]" is not assignable to "type[C[S@classmeth]]"
Type "type[C[T@C]]" is not assignable to type "type[C[S@classmeth]]"
Type parameter "T@C" is invariant, but "T@C" is not the same as "S@classmeth" (reportAssignmentType)
test.py:18:26 - error: Type "type[C[T@C]]" is not assignable to declared type "type[C[S@staticmeth]]"
"type[C[T@C]]" is not assignable to "type[C[S@staticmeth]]"
Type "type[C[T@C]]" is not assignable to type "type[C[S@staticmeth]]"
Type parameter "T@C" is invariant, but "T@C" is not the same as "S@staticmeth" (reportAssignmentType)
3 errors, 0 warnings, 0 informations
```
Notably, `reveal_type(C)` shows `type[C[Unknown]]` in all four contexts.
**Additional context**
I would like to `return object.__new__(C)` but then Pyright cannot infer the fact that `C` is used "at type `S`", and instead infers that we've passed a `type[C[Unknown]]` into `object.__new__` and have gotten a `C[Unknown]` back, which doesn't match with the expected return type `C[S]`.
I can work around this by first placing `C` into a type-annotated variable, thus
```py
ty: type[C[S]] = C
return object.__new__(ty)
```
but for some reason this only works in a standalone function context (hence this bug report).
Notably, if I do `return object.__new__(C[S])` this satisfies pyright, but instead makes cpython unhappy: at runtime we get `TypeError: object.__new__(X): X is not a type object (_GenericAlias)`. Same error for `return C[S].__new__(C[S])`... which is a little odd, because isn't this supposed to be equivalent to `C[S]()`?
**VS Code extension or command-line**
Pyright 1.1.403
Contributor guide
Research direction
Start with the test.py reproduction using Pyright 1.1.403 and compare the standalone function with the three methods. Locate the generic-class and method type-inference paths responsible for resolving C and S, then add regression coverage for all four contexts. Done means the annotated construction is accepted consistently without the reported assignment errors.
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