microsoft / pyright

Static Type Checker for Python
Other
13.04k stars 1.39k forks source link

Pyright detection error #8373

Closed 17Reset closed 1 month ago

17Reset commented 1 month ago

The code has been modified to add a type judgement if statement, why does pyright still detect the error?

Source Code:

        if self.canvas_tabwidget.tabBar() is not None:
            compiler_style_tabbar1(self.canvas_tabwidget.tabBar(), 96)
            self.canvas_tabwidget.tabBar().setContextMenuPolicy(
                Qt.ContextMenuPolicy.NoContextMenu
            )

pyright detection error:

g:\compiler\compiler_initial.py
  g:\compiler\compiler_initial.py:35:44 - error: "setContextMenuPolicy" is not a known attribute of "None" (reportOptionalMemberAccess)
erictraut commented 1 month ago

This is very unlikely to be a bug in pyright. It's probably a bug in the library or type stubs you're using. I'd be happy to help you diagnose if further if you'd provide a self-contained code sample that demonstrates the problem you're seeing. The code sample you've posted above refers to a bunch of symbols that are not defined and are not builtins.

I'm going to close the issue since it's probably not a bug, but feel free to post additional details, and I'll help you diagnose the problem.

17Reset commented 1 month ago

This is a minimal example of the source code:

import sys
from PyQt6.QtWidgets import QApplication
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QWidget, QTabWidget

class Compiler(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.canvas_tabwidget = QTabWidget(self)

        if self.canvas_tabwidget.tabBar() is not None:
            self.canvas_tabwidget.tabBar().setContextMenuPolicy(
                Qt.ContextMenuPolicy.NoContextMenu
            )

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Compiler()
    window.show()
    sys.exit(app.exec())

Here is the use of Pyright and the results:

C:\Users\xlab>pyright D:\Job\Programming\python\xTest\xPyQt6\pyright_demo\demo1.py
d:\Job\Programming\python\xTest\xPyQt6\pyright_demo\demo1.py
  d:\Job\Programming\python\xTest\xPyQt6\pyright_demo\demo1.py:14:44 - error: "setContextMenuPolicy" is not a known attribute of "None" (reportOptionalMemberAccess)
1 error, 0 warnings, 0 informations
17Reset commented 1 month ago

Add a line to print out the object:

import sys
from PyQt6.QtWidgets import QApplication
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QWidget, QTabWidget

class Compiler(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.canvas_tabwidget = QTabWidget(self)
        if self.canvas_tabwidget.tabBar() is not None:
            print(self.canvas_tabwidget.tabBar())
            self.canvas_tabwidget.tabBar().setContextMenuPolicy(
                Qt.ContextMenuPolicy.NoContextMenu
            )

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Compiler()
    window.show()
    sys.exit(app.exec())

result

<PyQt6.QtWidgets.QTabBar object at 0x000001B809C79270>
erictraut commented 1 month ago

The problem is that the tabBar method returns a value of type QTabBar | None. Type checkers cannot assume that calling a method twice will return the same value, so the check you're doing in the if statement has no bearing on the type returned by the second call to the same method. If you want this to type check without errors, you should assign the value to a variable and use that value in both the test and the subsequent dereference.

class Compiler(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.canvas_tabwidget = QTabWidget(self)

        tab_bar = self.canvas_tabwidget.tabBar()
        if tab_bar is not None:
            tab_bar.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu)
17Reset commented 1 month ago

Doesn't that make the code redundant?

17Reset commented 1 month ago

I think this is not the best way to design PyQt6 code, for the above example, QTabWidget call method tabBar(), is sure to return a QTabBar object, can't be None, so this detection is meaningless.