pyright icon indicating copy to clipboard operation
pyright copied to clipboard

Regression in v1.1.374 with two-argument form of `super()` **inside** a class

Open bram-tv opened this issue 6 months ago • 5 comments

Preamble

The release notes for pyright v1.1.374 contains:

Fixed bug that results in a false positive when using a two-argument form of super() outside of a class.

which appears to be in response to #8604 (and fixed by #8611).

This however appears to have caused a change in behavior with some code that uses super() inside a class and which is not yet fully typed. If the change is intended then feel free to close as not-a-bug.

(The errors below can also be fixed by adding more/the appropriate type hints so if the conclusion is more type-hints should be added then it can also be closed as not-a-bug.)

Describe the bug

Pyright now returns a different(/wrong) type for a classmethod call that uses the two-argument form of super().

Code or Screenshots

Minimal code example (real code is more complex and spread over multiple packages)

# pyright: strict


from typing import TypeVar, TYPE_CHECKING


_T = TypeVar("_T", bound="Model")


class Model:
    def __init__(self, key: str) -> None:
        self.key = key

    @classmethod
    def get(cls: type[_T], key: str) -> _T:
        return cls(key=key)


class Base(Model):
    @classmethod
    def get(cls, key: str = "BASE"):
        return super(Base, cls).get(key)


class Sub(Base):
    pass


def show(obj: Sub):
    print(obj.key)


sub = Sub.get()
print(sub.__class__)
show(sub)
if TYPE_CHECKING:
    reveal_type(sub)

base = Base.get()
print(base.__class__)
if TYPE_CHECKING:
    reveal_type(base)

pyright v1.1.373:

Running the code with Pyright v1.1.373:

$ PYRIGHT_PYTHON_FORCE_VERSION=1.1.373 poetry run pyright src/super.py
WARNING: there is a new pyright version available (v1.1.373 -> v1.1.376).
Please install the new version or set PYRIGHT_PYTHON_FORCE_VERSION to `latest`

.../src/super.py
  .../src/super.py:37:17 - information: Type of "sub" is "Sub"
  .../src/super.py:42:17 - information: Type of "base" is "Base"
0 errors, 0 warnings, 2 informations

pyright v1.1.374:

Running the code with Pyright v1.1.374:

$ PYRIGHT_PYTHON_FORCE_VERSION=1.1.374 poetry run pyright src/super.py
WARNING: there is a new pyright version available (v1.1.374 -> v1.1.376).
Please install the new version or set PYRIGHT_PYTHON_FORCE_VERSION to `latest`

.../src/super.py
  .../src/super.py:35:6 - error: Argument of type "Base*" cannot be assigned to parameter "obj" of type "Sub" in function "show"
    "Base*" is incompatible with "Sub" (reportArgumentType)
  ../src/super.py:37:17 - information: Type of "sub" is "Base*"
  ../src/super.py:42:17 - information: Type of "base" is "Base*"
1 error, 0 warnings, 2 informations

Avoiding the error

When the code is updated and the two-argument form of super() removed then pyright v1.1.374 shows the same output as pyright v1.1.373.

diff --git a/src/super.py b/src/super.py
index d4f81a3..4abac31 100644
--- a/src/super.py
+++ b/src/super.py
@@ -19,7 +19,7 @@ class Model:
 class Base(Model):
     @classmethod
     def get(cls, key: str = "BASE"):
-        return super(Base, cls).get(key)
+        return super().get(key)


 class Sub(Base):

Running again with pyright v1.1.374 (same output with v1.1.376):

PYRIGHT_PYTHON_FORCE_VERSION=1.1.374 poetry run pyright src/super.py
WARNING: there is a new pyright version available (v1.1.374 -> v1.1.376).
Please install the new version or set PYRIGHT_PYTHON_FORCE_VERSION to `latest`

.../src/super.py
  .../src/super.py:37:17 - information: Type of "sub" is "Sub"
  .../src/super.py:42:17 - information: Type of "base" is "Base"
0 errors, 0 warnings, 2 informations

Fixing the error

Really fixing the error can be done by adding more type-hints, i.e.

diff --git a/src/super.py b/src/super.py
index d4f81a3..f951d61 100644
--- a/src/super.py
+++ b/src/super.py
@@ -5,6 +5,7 @@ from typing import TypeVar, TYPE_CHECKING


 _T = TypeVar("_T", bound="Model")
+_B = TypeVar("_B", bound="Base")


 class Model:
@@ -18,7 +19,7 @@ class Model:

 class Base(Model):
     @classmethod
-    def get(cls, key: str = "BASE"):
+    def get(cls: type[_B], key: str = "BASE") -> _B:
         return super(Base, cls).get(key)


Running again with pyright v1.1.374 (same output with v1.1.376)

$ PYRIGHT_PYTHON_FORCE_VERSION=1.1.374 poetry run pyright src/super_fixed2.py
WARNING: there is a new pyright version available (v1.1.374 -> v1.1.376).
Please install the new version or set PYRIGHT_PYTHON_FORCE_VERSION to `latest`

.../src/super.py
  .../src/super.py:38:17 - information: Type of "sub" is "Sub"
  .../src/super.py:43:17 - information: Type of "base" is "Base"
0 errors, 0 warnings, 2 informations

bram-tv avatar Aug 14 '24 18:08 bram-tv