mottosso / Qt.py

Minimal Python 2 & 3 shim around all Qt bindings - PySide, PySide2, PyQt4 and PyQt5.
MIT License
910 stars 254 forks source link

Qt.py Linter #124

Open mottosso opened 8 years ago

mottosso commented 8 years ago

Goal

Discover binding incompatibilities in your code through linting.

$ Qt.py --lint my_module.py
#24:my_widget = QtGui.QWidget(): Should be QtWidgets.QWidget()
#103:header.setResizeMode(header.Fixed): setResizeMode has been renamed setSectionResizeMode in Qt 5.
#244: ...

Motivation

In lieu of warning messages due to technical constraints, how about an external process that can either be run as-is on your new or existing code, or integrated into a realtime linter, such as SublimeLinter?

Implementation wise it would be rather straightforward; simply look for common patterns where something is used that shouldn't. We'd have to keep in mind more esoteric use, such as import QtWidgets as MyCustomStuff which would complicate things, but at least we can help a majority. (And eventually do handle such cases gracefully).

fredrikaverpil commented 8 years ago

This would be a killer feature.

melevine commented 7 years ago

I looked into doing something like this a few different ways. One is as a pylint-plugin of sorts. pylint uses ast, which breaks up the code into nodes using Python's builtin grammar engine, then walks the grammar tree. That's probably the 'right' way to do something like this. But for this use case, it might be overkill, and a series of simple regular expressions that look for common issues might be a better solution for this feature.

At least in my mind, this linter is mainly going to be used for converting Qt4 code to a Qt5 compliant API. With that in mind, I see this as powerful, but seldom used conversion aid. But, a good way to catch obvious problems, and to flag less obvious problems, like using QString and QVariant objects, deprecated methods, etc. eq. value = self.model().data(0, Qt.UserRole).toPyObject() The type of problems you will encounter at run time and can only be discovered by exercising the code, or by carefully examining the code.

fredrikaverpil commented 7 years ago

Your insights are greatly appreciated @melevine as at least myself has never tried to write a linter. In case you feel like taking a stab at it, feel free to do so :)

chadrik commented 7 years ago

One way to achieve this is to provide PEP484-compatible pyi stub files with the project or within the official python typeshed. Then you run a program like mypy to do static type checking. It checks for missing objects/attributes and also incorrect types, where possible.

PyCharm also supports pyi files as of version 2017. Support in other IDEs is still lagging, but now that type hints are an official part of python -- including syntax support added in python versions 3.5 and 3.6 -- I think you'll see the community converging on this format. The mypy project is headed up by Guido Van Rossum, btw. And yes, pyi stubs work in python 2.

PyQt provides them as a build option for python3 but it turns out that these are not working quite right. The author of QtPy is working on pyi stubs for their project. You may be able to "borrow" them.

I should point out that, for better or for worse, mypy will check for far more than just incorrect usage of Qt.py, so when a user runs it against their code it will likely generate many additional errors, unrelated to Qt.py. The plus side is that those might be real errors, and the typing info is super useful for realtime feedback within IDEs.

Btw, while I have you, where can I find a description of the differences between Qt.py and QtPy?

mottosso commented 7 years ago

Thanks @chadrik.

Btw, while I have you, where can I find a description of the differences between Qt.py and QtPy?

There's a summary here, but nothing more in-depth exists at the moment.

https://github.com/mottosso/Qt.py#projects-similar-to-qtpy