Why do we have a __feature__?#
In PySide user story PYSIDE-1019, we tested certain ways to
make PySide more pythonic. The first idea was to support some
way to allow for
snake_case function names.
This feature is possible with relatively low compatibility problems, because having the same function with different names would be not so nice, but a possible low-effort solution.
When going to
true_property, things become different. When we
support properties as first class objects instead of getter
and setter functions, we get a conflict, because a function
cannot act as a property (no braces) and be a function at the
This consideration led us to the idea: Features must be selectable per-module.
Why are features selectable per-module?#
Suppose you have some pre-existing code. Maybe you use some downloaded code or you generated an interface file. When you now decide to use a feature, you don’t want all this existing stuff to become incorrect. By using the statement
from __feature__ import ...
you declare that this module uses some feature. Other modules will not be influenced by this decision and can stay unchanged.
Why dunder, and why not __future__?#
Especially in Python 2, but in a few cases also in Python 3, there is the future statement
from __future__ import ...
That is a statement that can only appear at the beginning of a module, and it switches how the Python parser works.
Our first idea was to mimick this behavior for PySide, although we are a bit cheating: The feature statement is not a syntactical construct, and we cannot easily forbid that it is in the middle of a module.
We then realized that the intention of Python’s
__future__ import and
__feature__ import are different: While Python implies by
__future__ some improvement, we do not want to associate with
__feature__. We simply think that some users who come from Python may
like our features, while others are used to the C++ convention and
consider something that deviates from the Qt documentation as drawback.
The intention to use the
from __feature__ import ... notation was the hope that
people see the similarity to Python’s
__future__ statement and put that import
at the beginning of a module to make it very visible that this module
has some special global differences.
The snake_case feature#
By using the statement
from __feature__ import snake_case
all methods of all classes used in this module are changing their name.
The algorithm to change names is this:
if the name has less than 3 chars, or
if two upper chars are adjacent, or
if the name starts with
gl(which marks OpenGL),
the name is returned unchanged. Otherwise
a single upper char
Cis replaced by
The true_property feature#
By using the statement
from __feature__ import true_property
all methods of all classes used in this module which are declared in the Qt documentation as property become real properties in Python.
This feature is incompatible with the past and cannot coexist; it is the reason why the feature idea was developed at all.
Normal properties have the same name as before:
becomes as property
When there is also a setter method,
becomes as property
QtWidgets.QLabel().color = value
Normal properties swallow the getter and setter functions and replace them by the property object.
Special properties are those with non-standard names.
becomes as property
But here we have no setSize function, but
which becomes as property
QtWidgets.QLabel().size = value
In that case, the setter does not become swallowed, because so many
people are used to the
It should be mentioned that we not only support regular properties as they are known from Python. There is also the concept of class properties which always call their getter and setter:
A regular property like the aforementioned
>>> QtWidgets.QLabel.size <property object at 0x113a23540> >>> QtWidgets.QLabel().size PySide6.QtCore.QSize(640, 480)
A class property instead is also evaluated without requiring an instance:
>>> QtWidgets.QApplication.windowIcon <PySide6.QtGui.QIcon(null) at 0x113a211c0>
You can only inspect it if you go directly to the right class dict:
>>> QtGui.QGuiApplication.__dict__["windowIcon"] <PySide6.PyClassProperty object at 0x114fc5270>
About Property Completeness#
There are many properties where the Python programmer agrees that these functions should be properties, but a few are not properties, like
>>> QtWidgets.QMainWindow.centralWidget <method 'centralWidget' of 'PySide6.QtWidgets.QMainWindow' objects>
We are currently discussing if we should correct these rare cases, as they are probably only omissions. Having to memorize the missing properties seems to be quite cumbersome, and instead of looking all properties up in the Qt documentation, it would be easier to add all properties that should be properties and are obviously missing.
Name Clashes and Solution#
There are some rare cases where a property already exists as a function, either with multiple signatures or having parameters. This is not very nice in C++ as well, but for Python this is forbidden. Example:
>>> from PySide6 import * >>> import pprint >>> pprint.pprint(QtCore.QTimer.singleShot.__signature__) [<Signature (arg__1: int, arg__2: Callable) -> None>, <Signature (msec: int, receiver: PySide6.QtCore.QObject, member: bytes) -> None>, <Signature (msec: int, timerType: PySide6.QtCore.Qt.TimerType, receiver: PySide6.QtCore.QObject, member: bytes) -> None>]
When creating this property, we respect the existing function and use a slightly different name for the property by appending an underscore.
>>> from __feature__ import true_property >>> QtCore.QTimer.singleShot_ <property object at 0x118e5f8b0>
We hope that these clashes can be removed in future Qt versions.
The __feature__ import#
The implementation of
from __feature__ import ... is built by a slight
modification of the
__import__ builtin. We made that explicit by assigning
variables in the builtin module. This modification takes place at Qt for Python
The original function in
__import__is kept in
The new function is in
__feature_import__and assigned to
This function calls the Python function
first, and falls back to
__orig_import__ if feature import is not applicable.
This is not recommended. Import modifications should be done using import hooks, see the Python documentation on Import-Hooks.
If you would like to modify
__import__ anyway without destroying the features,
please override just the
IDEs and Modifying Python stub files#
Qt for Python comes with pre-generated
.pyi stub files in the same location as
the binary module. For instance, in the site-packages directory, you can find
QtCore.pyi file next to
QtCore.pyd on Windows.
__feature__ often with common IDEs, you may want to provide
a feature-aware version of
.pyi files to get a correct display. The simplest
way to change them all in-place is the command:
pyside6-genpyi all --feature snake_case true_property
Using __feature__ with UIC files#
Features can be freely used together with generated UIC files. The UIC files are _not_ converted, intentionally. Mixing them with feature selections in other Python modules should always work, because switching will happen as needed, selected by the currently active module. (Please report to us if this fails for an example)