microsoft / pyright

Static Type Checker for Python
Other
13.12k stars 1.4k forks source link

Pyright 1.1.375 hangs on file with overloaded functions using Python 3.12 (PEP 695) generics #8687

Closed Antyos closed 1 month ago

Antyos commented 1 month ago

Describe the bug Pyright is having issues parsing the overloaded functions with generic type arguments shown below. Analyzing with pyright test.py using Pyright 1.1.373 works fine, however Pyright 1.1.375 hangs. (In Pyright 1.1.374, builtin types cannot be detected, but I suspect this was fixed in 1.1.375)

Code or Screenshots Using a Python 3.12.4 virtual environment on Windows 11 with only Pyright installed.

# test.py
from typing import Callable, Union, overload

@overload
def map_nested[K, VI, VO](d: dict[K, VI], func: Callable[[VI], VO]) -> dict[K, VO]: ...

# This is the problematic overload:
@overload
def map_nested[K, VI, VO](d: VI, func: Callable[[VI], VO]) -> VO: ...

# This overload also fails:
# @overload
# def map_nested[_K, _VI, _VO](d: _VI, func: Callable[[_VI], _VO]) -> _VO: ...

# But this overload works:
# _K = TypeVar("_K")
# _VI = TypeVar("_VI")
# _VO = TypeVar("_VO")
# @overload
# def map_nested(d: _VI, func: Callable[[_VI], _VO]) -> _VO: ...

def map_nested[K, VI, VO](
    d: Union[dict[K, VI], VI], func: Callable[[VI], VO]
) -> Union[dict[K, VO], VO]:
    """Map a function over a nested dictionary.

    Examples
    --------
    >>> d = {"a": {"b": 1, "c": [2, {"d": 4}]}, "e": 5}
    >>> map_nested_dict(d, lambda x: x + 1)
    {"a": {"b": 2, "c": [3, {"d": 5}]}, "e": 6}
    """
    if not isinstance(d, dict):
        return func(d)
    new_dict = dict()
    for key, val in d.items():
        if isinstance(val, dict):
            new_dict[key] = map_nested(val, func)
        elif isinstance(val, list):
            new_dict[key] = [map_nested(item, func) for item in val]
        else:
            # This error has to do with the Union in the type hint, but I've always ignored it. May be worth opening another issue.
            new_dict[key] = func(val) # pyright: ignore[reportArgumentType]
    return new_dict

Command line:

> pyright test.py --verbose
No include entries specified; assuming c:\Users\Andrew\Code\pyright_test
Auto-excluding **/node_modules
Auto-excluding **/__pycache__
Auto-excluding **/.*
Search paths for file:///c%3A/Users/Andrew/Code/pyright_test
  c:\Users\Andrew\.cache\pyright-python\1.1.375\node_modules\pyright\dist\typeshed-fallback\stdlib
  c:\Users\Andrew\Code\pyright_test
  c:\Users\Andrew\Code\pyright_test\typings
  c:\Users\Andrew\.cache\pyright-python\1.1.375\node_modules\pyright\dist\typeshed-fallback\stubs\...
  c:\Users\Andrew\AppData\Local\Programs\Python\Python312\DLLs
  c:\Users\Andrew\AppData\Local\Programs\Python\Python312\Lib
  c:\Users\Andrew\AppData\Local\Programs\Python\Python312
  c:\Users\Andrew\Code\pyright_test\.venv
  c:\Users\Andrew\Code\pyright_test\.venv\Lib\site-packages
Found 1 source file

# Hangs here if the overloads are uncommented as noted above.

VS Code extension or command-line Running Pyright 1.1.375 via the command line in a virtual environment.

erictraut commented 1 month ago

Thanks for the bug report, and apologies for the regression. I'm able to repro and have it isolated to a particular commit. I'll continue to investigate.

erictraut commented 1 month ago

This is addressed in pyright 1.1.376.