'Wrong method template for PySide2.QtCore.QAbstractItemModel.parent(): where is the problem?

I am trying to implement a subclass of QAbstractItemModel using PySide2.

The signature of the parent method is

virtual QModelIndex parent(const QModelIndex &index) const = 0

PyCharm (community 2019.3.4 as well as 2019.2.5, and professional 2019.3) expects this signature:

import PySide2
from PySide2.QtCore import QAbstractItemModel

class MyModel(QAbstractItemModel):
    def parent(self) -> PySide2.QtCore.QObject:
        return super().parent()

In this template, parent() has no argument, which doesn't seem to make any sense.

When I switch to PyQt5, the auto-generated method template is as expected:

from PyQt5.QtCore import QAbstractItemModel

class MyModel(QAbstractItemModel):

    def parent(self, QModelIndex=None):
        return super().parent(QModelIndex)

The PySide2 documentation again shows a correct pattern:

PySide2.QtCore.QAbstractItemModel.parent(child)

Parameters

    child – QModelIndex
Return type

    QModelIndex

I tried PySide 5.14.1 as well as 5.13.2.

What is going on here? Is it a bug in PySide2 or PyCharm?



Solution 1:[1]

IMHO think that in both cases (PyQt5 and PySide2) the PyCharm linter has a bug.

  • QAbstractItemModel is a QObject so it has the same QObject methods, and QObject has the parent() method (which is part of the hierarchy between QObject):

QObject *QObject::parent() const

Returns a pointer to the parent object.

  • Also QAbstractItemModel handles the hierarchy between the QModelIndex so it needs a parent(const QModelIndex &) method that provides the QModelIndex parent of another QModelIndex:

QModelIndex QAbstractItemModel::parent(const QModelIndex &index) const

Returns the parent of the model item with the given index. If the item has no parent, an invalid QModelIndex is returned.

A common convention used in models that expose tree data structures is that only items in the first column have children. For that case, when reimplementing this function in a subclass the column of the returned QModelIndex would be 0.

When reimplementing this function in a subclass, be careful to avoid calling QModelIndex member functions, such as QModelIndex::parent(), since indexes belonging to your model will simply call your implementation, leading to infinite recursion.

Note: This function can be invoked via the meta-object system and from QML. See Q_INVOKABLE.


In other words, QAbstractItemModel has 2 parent() methods(with different signature): The first allows obtaining the QObject parent of the model (since it is a QObject), and the second is the one that provides the QModelIndex parent of another QModelIndex that the model handles.


(Disclaimer: I don't use Pycharm or another IDE for the autocomplete) IMHO Pycharm should provide both autocompletes for the user to select which of the methods they want to override (noting that the first is a public method and the other an abstract method).

It should be noted that it is not a PySide2 or PyQt5 bug since that library does not provide the autocomplete but Pycharm. As a final note I recommend reading the Qt docs.

Public methods are not overridden(in general) so the def parent(self, QModelIndex=None): autocomplete is the most appropriate since it is an abstract method that must be override if you want to create instances of that class.

Solution 2:[2]

[Two years later...] I had the same issue today, with more recent versions of both PyCharm (2022.1) and PySide (6.2.4).

This is definitely due to PyCharm, which doesn't make full use of PySide's .pyi files.

PyCharm has an "implements/overrides" indicator in the left gutter, and clicking it brings you to the definition of the parent method you're implementing or overriding.

In our case, I ended up in a Python Interface file, QtCore.pyi (in my case, located in .../venv/Lib/site-packages/PySide6/QtCore.pyi)

Thanks to @eyllanesc's answer, I wasn't surprised to see that two declarations had been generated there:

    @overload
    def parent(self) -> PySide6.QtCore.QObject: ...
    @overload
    def parent(self, child:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex]) -> PySide6.QtCore.QModelIndex: ...

PyCharm seems to only take the first occurrence into account, either when autocompleting, or when checking the code.

Fixing it quickly is pretty easy : just swap the two declarations, and PyCharm will both autocomplete correctly (no need to reindex), and quit complaining if you had already written the correct signature manually .

I don't know how to fix it "cleanly", though, so I won't expand too much on that ; it would probably require reporting a bug, but I'm not sure to whom, since some search engine results suggest that they are generated by PyCharm itself, while others suggest that they are part of the distributed package.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Artesim