pyright
pyright copied to clipboard
Constructor call within generic class confuses type arguments
A regression introduced in 1.1.337 breaks the following:
from typing import Generic, TypeVar
T = TypeVar("T")
U = TypeVar("U")
class Test2(Generic[T, U]):
def __init__(self):
pass
def test2(self) -> None:
x1: Test2[U, T]
x1 = Test2[U, T]()
This should have been caught by the constructor26.py unit test, but it was not.
This is related to https://github.com/microsoft/pyright/issues/7369.
I am not sure if this is related, but recently this started to raise "type is partially unknown" error:
from typing import Generic, Self, TypeVar
T = TypeVar("T")
class Test(Generic[T]):
@classmethod
def make(cls, type_: type[T]) -> Self:
raise NotImplementedError()
t = Test.make(int)
in previous versions t
was recognized as Test[int]
, but now it shows Test[Unknown]
and two errors are reported:
Type of "make" is partially unknown
Type of "make" is "(type_: Unknown) -> Test[Unknown]"
and
Type of "t" is partially unknown
Type of "t" is "Test[Unknown]"
I am not sure if this is related
This isn't related. The behavior you're seeing here was due to a recent change for spec compliance. The current behavior is correct. Since you haven't specified a type argument for the Test
class, it defaults to Unknown
when binding Test
to the make
class method.
Okay, but isn't the type_
parameter with type T
forcing return type to also be specialized with T
?
Do you know what is the current approach to specialize a classmethod returning Self
with concrete type from one of its arguments?
Do you know what is the current approach to specialize a classmethod returning Self with concrete type from one of its arguments?
If you want to use a classmethod in this manner, you would need explicitly specialize the class that you're binding to the class method.
t = Test[int].make(int)
Or if you're using the new PEP 696 TypeVar defaults, you can rely on the default value of the TypeVar.
from typing import Generic, Self
from typing_extensions import TypeVar
T = TypeVar("T", default=int)
class Test(Generic[T]):
@classmethod
def make(cls, type_: type[T]) -> Self:
raise NotImplementedError()
t = Test.make(int)
Okay, can you provide more details (any source to read more details) on change for spec compliance
? mypy in current version reveals type correctly (as pyright in previous versions) and does not require this double typing. I would like to understand in which scenario (and why) the same T
in type_: type[T]
and Generic[T]
refer to different types, so return type is unknown.
We're quite off topic for this issue, but since you asked... This was previously an under-specified (and somewhat ambiguous) part of the typing spec, but the recent adoption of PEP 696 clarifies how this must work. The PEP 696 specification was recently incorporated into the official Python typing spec. The latest published version of mypy (1.9) doesn't have support for PEP 696, but the next version (1.10) will have partial (possibly complete) support.
This is addressed in pyright 1.1.373.