microsoft/pyright

False inconsistent overloads error

Closed this issue · 1 comments

Describe the bug
Pyright reports that my overloads are not consistent. I expect successful typechecking. Didn't happen on Pyright v1.1.363.

PS D:\Projects\Python\pyvolt> py -m pyright .\pyright_bug.py
d:\Projects\Python\pyvolt\pyright_bug.py
  d:\Projects\Python\pyvolt\pyright_bug.py:23:9 - error: Overloaded implementation is not consistent with signature of overload 2
    Type "(self: Self@UserSettings, key: str, *default: str) -> (str | None)" is incompatible with type "(self: Self@UserSettings, key: str, default: str) -> str"
      Missing keyword parameter "default" (reportInconsistentOverload)
  d:\Projects\Python\pyvolt\pyright_bug.py:39:19 - information: Type of "setting1" is "str | None"
  d:\Projects\Python\pyvolt\pyright_bug.py:42:19 - information: Type of "setting2" is "str"
1 error, 0 warnings, 2 informations

Code or Screenshots

Playground

from datetime import datetime
import typing as t


class UserSettings:
    """The user settings."""

    value: dict[str, tuple[int, str]]
    """The {user_setting_key: (timestamp, value)} mapping."""

    def __init__(self, value: dict[str, tuple[int, str]]) -> None:
        self.value = value

    def __getitem__(self, key: str) -> str:
        return self.value[key][1]

    @t.overload
    def get(self, key: str) -> str | None: ...

    @t.overload
    def get(self, key: str, default: str) -> str: ...

    def get(self, key: str, *default: str) -> str | None:
        """Get a user setting."""
        if key in self.value:
            return self.value[key][1]
        return default[0] if default else None


if __name__ == "__main__":
    settings = UserSettings(
        {
            "theme": (1, "dark"),
            "locale": (2, "uk-UA"),
        }
    )

    setting1 = settings.get("theme")
    t.reveal_type(setting1)
    print(setting1)
    setting2 = settings.get("locale", "en-US")
    t.reveal_type(setting2)
    print(setting2)

VS Code extension or command-line
Using command-line tool. Tested on 1.1.364 and got same errors.

PS D:\Projects\Python\pyvolt> py -m pip show pyright
Name: pyright
Version: 1.1.365
Summary: Command line wrapper for pyright
Home-page: https://github.com/RobertCraigie/pyright-python
Author: Robert Craigie
Author-email:
License: MIT
Location: C:\Users\mclr\AppData\Local\Programs\Python\Python312\Lib\site-packages
Requires: nodeenv
Required-by:

Pyright's new behavior is correct. Older versions contained a bug. The problem is that your second overload signature accepts a parameter default by keyword, but your implementation does not.

Here's a simplified example that demonstrates why this is a problem.

from typing import overload

@overload
def func() -> str | None: ...

@overload
def func(default: str) -> str: ...

def func(*default: str) -> str | None:
    pass

func(default="") # Crashes at runtime!

Here is how I would recommend fixing your implementation to avoid the bug:

    def get(self, key: str, default: str | None = None) -> str | None:
        if key in self.value:
            return self.value[key][1]
        return default