microsoft / microsoft/pyright

Referring to a different instantiation whilst defining a generic class

Open
#10,778 4 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

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

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.