Don Kirkby
Don Kirkby

Reputation: 56230

How does the new snake_case feature work in PySide6?

I'm upgrading from PySide2 version 5 to PySide6, and the release notes say that it supports snake-case method names as well as replacing getter and setter methods with properties. That sounds like a big improvement, but I can't figure out how to enable it. The release notes have a code sample, but it's not runnable. When I try to expand it into a runnable sample, the new version doesn't work.

Here's the old style that still works with PySide6:

import sys
from PySide6.QtWidgets import (QTableWidget, QPushButton, QVBoxLayout,
                               QApplication, QWidget)


class MyWidget(QWidget):
    def __init__(self):
        super().__init__()

        table = QTableWidget()
        table.setColumnCount(2)

        button = QPushButton("Add")
        button.setEnabled(False)

        layout = QVBoxLayout(self)
        layout.addWidget(table)
        layout.addWidget(button)


if __name__ == "__main__":
    app = QApplication([])

    widget = MyWidget()
    widget.resize(800, 600)
    widget.show()

    sys.exit(app.exec_())

Here's the new version that doesn't work:

from __feature__ import snake_case, true_property

import sys
from PySide6.QtWidgets import (QTableWidget, QPushButton, QVBoxLayout,
                               QApplication, QWidget)


class MyWidget(QWidget):
    def __init__(self):
        super().__init__()

        table = QTableWidget()
        table.column_count = 2

        button = QPushButton("Add")
        button.enabled = False

        layout = QVBoxLayout(self)
        layout.add_widget(table)
        layout.add_widget(button)


if __name__ == "__main__":
    app = QApplication([])

    widget = MyWidget()
    widget.resize(800, 600)
    widget.show()

    sys.exit(app.exec_())

When I run the new version, I get this error:

Traceback (most recent call last):
File "/home/don/.config/JetBrains/PyCharm2021.1/scratches/scratch2.py", line 1, in <module>
from __feature__ import snake_case, true_property
ModuleNotFoundError: No module named '__feature__'

I'm not surprised that breaks, where the heck is __feature__ supposed to come from? I tried switching it to __future__, but that didn't work either. Just removing the __feature__ line doesn't work either.

Upvotes: 5

Views: 2758

Answers (2)

Chris
Chris

Reputation: 2860

One important point is that for propper IDE auto-completion support one has to re-generate the python stubs files to reflect the selected notation. This is done by calling:

pyside6-genpyi all --feature snake_case true_property

This will rewrite the stubs file used to lookup methods and their types.

It still needs to be activated with the __feature__ tag after importing PySide:

import PySide6
from __feature__ import snake_case, true_property

...

Full documentation: https://doc.qt.io/qtforpython-6/developer/feature-motivation.html

Upvotes: 4

Don Kirkby
Don Kirkby

Reputation: 56230

I found the hint in what looks like the original feature description:

The decision depends of the following setting at the beginning of a module after PySide2 import:

from __feature__ import snake_case

The key thing that I had missed was that you have to place that after the PySide2 import. With further experimenting, I found that you have to place it after all other imports, not just PySide6. Any later import will reset the feature flags.

When I move that line, the new version works. My IDE hates it, though, so I have to turn off validation for that line:

import sys
from PySide6.QtWidgets import (QTableWidget, QPushButton, QVBoxLayout,
                               QApplication, QWidget)

# noinspection PyUnresolvedReferences
from __feature__ import snake_case, true_property


class MyWidget(QWidget):
    def __init__(self):
        super().__init__()

        table = QTableWidget()
        table.column_count = 2

        button = QPushButton("Add")
        button.enabled = False

        layout = QVBoxLayout(self)
        layout.add_widget(table)
        layout.add_widget(button)


if __name__ == "__main__":
    app = QApplication([])

    widget = MyWidget()
    widget.resize(800, 600)
    widget.show()

    sys.exit(app.exec_())

Upvotes: 8

Related Questions