Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUG: upgrade 1.1.392 -> 1.1.393: can not resolve @overload __init__() #9784

Closed
Andrei-Pozolotin opened this issue Jan 29, 2025 · 9 comments
Closed
Labels
addressed in next version Issue is fixed and will appear in next published version bug Something isn't working regression

Comments

@Andrei-Pozolotin
Copy link

The PySide6 library : https://pypi.org/project/PySide6/6.8.1.1/

Comes with the following @overload __init__() definitions in .../PySide6/QtGui.pyi:

class QKeySequence(Shiboken.Object):
    @typing.overload
    def __init__(self) -> None: ...
    @typing.overload
    def __init__(self, ks: PySide6.QtGui.QKeySequence) -> None: ...
    @typing.overload
    def __init__(self, key: PySide6.QtGui.QKeySequence.StandardKey) -> None: ...
    @typing.overload
    def __init__(self, k1: PySide6.QtCore.QKeyCombination | PySide6.QtCore.Qt.KeyboardModifier | PySide6.QtCore.Qt.Key, k2: PySide6.QtCore.QKeyCombination | PySide6.QtCore.Qt.KeyboardModifier | PySide6.QtCore.Qt.Key= ..., k3: PySide6.QtCore.QKeyCombination | PySide6.QtCore.Qt.KeyboardModifier | PySide6.QtCore.Qt.Key= ..., k4: PySide6.QtCore.QKeyCombination | PySide6.QtCore.Qt.KeyboardModifier | PySide6.QtCore.Qt.Key= ...) -> None: ...
    @typing.overload
    def __init__(self, key: str, format: PySide6.QtGui.QKeySequence.SequenceFormat = ...) -> None: ...
    @typing.overload
    def __init__(self, k1: int, k2: int = ..., k3: int = ..., k4: int = ...) -> None: ...

The following user code:

PressEntry_QT: TypeAlias = (
    QKeySequence | QKeySequence.StandardKey | QtCore.QKeyCombination | str | int
)
press_entry: PressEntry_QT = "Ctrl+Enter"
press_keyseq = QKeySequence(press_entry)

is working fine with 1.1.392, but results in error with 1.1.393:

.../qt_keys.py:15:24 - error: No overloads for "__init__" match the provided arguments (reportCallIssue)
.../qt_keys.py:15:37 - error: Argument of type "PressEntry_QT" cannot be assigned to parameter "k1" of type "int" in function "__init__"
    Type "PressEntry_QT" is not assignable to type "int"
      "QKeyCombination" is not assignable to "int" (reportArgumentType)
@Andrei-Pozolotin Andrei-Pozolotin added the bug Something isn't working label Jan 29, 2025
@erictraut
Copy link
Collaborator

Could you please provide a more complete example? The code above contains a bunch of symbols that are not defined or imported. I don't know which library (or library version) these come from.

Pyright 1.1.393 includes some changes to the overload call evaluation logic, which is probably the explanation for the change in behavior that you're seeing. We're working to standardize the behavior for calls to overloaded functions, and the latest version of pyright follows (or at least attempts to follow) the latest draft spec in this regard.

@erictraut erictraut added the question Further information is requested label Jan 29, 2025
@Andrei-Pozolotin
Copy link
Author

Thank you for looking into this.

Here is a more complete example: pyright_9784.py

from typing import TypeAlias

from PySide6 import QtCore
from PySide6 import QtGui
from PySide6.QtGui import QKeySequence


PressEntry_QT: TypeAlias = (
    QKeySequence | QKeySequence.StandardKey | QtCore.QKeyCombination | str | int
)


class AnyKeyNav_QT:

    @classmethod
    def render_press_entry_QT(cls, press_entry: PressEntry_QT) -> str:
        press_keyseq = QKeySequence(press_entry)
        press_render = press_keyseq.toString(QKeySequence.SequenceFormat.NativeText)
        return press_render

    @classmethod
    def render_press_event_QT(cls, press_event: QtGui.QKeyEvent) -> str:
        press_render = cls.render_press_entry_QT(press_event.key())
        return press_render

with error output report in version 1.1.393:

XXX/src/pyright_9784.py
  XXX/src/pyright_9784.py:17:24 - error: No overloads for "__init__" match the provided arguments (reportCallIssue)
  XXX/src/pyright_9784.py:17:37 - error: Argument of type "PressEntry_QT" cannot be assigned to parameter "k1" of type "int" in function "__init__"
    Type "PressEntry_QT" is not assignable to type "int"
      "QKeyCombination" is not assignable to "int" (reportArgumentType)
2 errors, 0 warnings, 0 informations 

where line 17 is the __init__() invocation:

        press_keyseq = QKeySequence(press_entry)

and pyproject.toml configuration section is:

[tool.pyright]
verboseOutput = true
pythonVersion = "3.12"
venv = ".venv"
venvPath = "."
include = [
    "src/**/*.py",
]
exclude = [
    "**/fc_lib/vendor/**",
]
reportUnusedImport = true
reportMissingImports = true
reportSelfClsParameterName = false
# https://github.com/spyder-ide/qtpy
[tool.pyright.defineConstant]
DEBUG = true
PYQT5 = false
PYQT6 = false
PYSIDE2 = false
PYSIDE6 = true

The PySide6 library was installed as this system package:
https://archlinux.org/packages/extra/x86_64/pyside6/

with python imports coming from these interface files:

/usr/lib/python3.13/site-packages/PySide6/QtGui.pyi
/usr/lib/python3.13/site-packages/PySide6/QtCore.pyi

which could be downloaded and extracted from pypi.org:
https://pypi.org/project/PySide6/#PySide6-6.8.1.1-cp39-abi3-manylinux_2_28_x86_64.whl

@erictraut
Copy link
Collaborator

Thanks for the additional details. I'm able to repro the problem, and on first analysis it does appear to be a bug. I'll investigate further.

@erictraut erictraut added regression and removed question Further information is requested labels Jan 29, 2025
@erictraut
Copy link
Collaborator

erictraut commented Jan 30, 2025

Here's what's going on here. The typing spec update proposes that type checkers should perform "type expansion" for arguments whose types include enums when evaluating the type of a call to an overloaded function. An enum is equivalent to a union of its constituent literal members.

The updated spec also notes that argument type expansion can result in unions with a large number of subtypes, and type checkers may choose to limit this to some reasonable number for performance reasons. Pyright's internal limit is currently 64.

In your code sample, the type PressEntry_QT is a union with five subtypes. However, one of those subtypes is an enum that expands to 71 literal members. Once all of these are expanded, the number of subtypes in the union exceeds the 64-subtype limit, and pyright doesn't perform any expansion.

I'll need to think about how best to handle this. I may need to do "progressive expansion" (first expanding unions, then expanding enums, tuples, etc.). This would complicate an already-complicated algorithm. I may also need to increase the internal limit from 64 to some larger number, but that won't fully address this problem.

@carljm, @JelleZijlstra just FYI... This raises an interesting point about the current draft update to the typing spec that I hadn't considered when I wrote the proposal.

@JelleZijlstra
Copy link
Contributor

I think progressive expansion makes sense conceptually, but I don't think we need to spell that out in the typing check; it can just be a part of how type checkers implement this limit.

@carljm
Copy link

carljm commented Jan 30, 2025

I agree with @JelleZijlstra ; I think the proposed spec has enough detail on the conceptually correct algorithm. Progressive expansion seems like part of implementing performance-based limits on that algorithm (if there's no limit specified in the spec / in principle, there's also no need for progressive expansion); that seems like it can be left up to individual type checkers as an implementation question.

@Andrei-Pozolotin
Copy link
Author

@erictraut :

Once all of these are expanded, the number of subtypes in the union exceeds the 64-subtype limit, and pyright doesn't perform any expansion.

RE: pyright doesn't perform any expansion:
if the solution will include any limit which breaks the algorithm,
then perhaps there should be a prescient warning issued to the user.

erictraut added a commit that referenced this issue Feb 8, 2025
…ing a call expression that targets an overloaded method and one of the arguments is an enum with more than 64 members. This addresses #9784.
erictraut added a commit that referenced this issue Feb 8, 2025
…ing a call expression that targets an overloaded method and one of the arguments is an enum with more than 64 members. This addresses #9784. (#9858)
@erictraut erictraut added the addressed in next version Issue is fixed and will appear in next published version label Feb 8, 2025
@erictraut
Copy link
Collaborator

This is addressed in pyright 1.1.394.

@Andrei-Pozolotin
Copy link
Author

This now works in 1.1.394, thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addressed in next version Issue is fixed and will appear in next published version bug Something isn't working regression
Projects
None yet
Development

No branches or pull requests

4 participants