Supposons que je crée une classe Python régulière qui possède une propriété, et dans l'implémentation de cette propriété, je fais une erreur qui mène à une erreur AttributeError. Un MVCE est la suivante:Message d'erreur trompeur lorsque AttributeError est déclenché dans la propriété de classe dérivée de QObject
class MyClass():
@property
def myProp(self):
raise AttributeError("my mistake")
def main():
# Gives a 'expected-error-message' as expected
myObject = MyClass()
print("Regular object property: {}".format(myObject.myProp))
if __name__ == "__main__":
main()
Cela donne l'erreur suivante, comme prévu:
Traceback (most recent call last):
File "prop_regular.py", line 14, in <module>
main()
File "prop_regular.py", line 10, in main
print("Regular object property: {}".format(myObject.myProp))
File "prop_regular.py", line 5, in myProp
raise AttributeError("my mistake")
AttributeError: my mistake
Cependant, si je laisse le Hériter de la classe de QObject, l'erreur est source de confusion. Par exemple exécutant le code suivant
from PyQt5 import QtCore
class MyQtClass(QtCore.QObject):
@property
def myProp(self):
raise AttributeError("my-mistake")
def main():
app = QtCore.QCoreApplication([])
# Gives confusing error message: 'MyQtClass' object has no attribute 'myProp'
qc = MyQtClass()
print("Qt object property: {}".format(qc.myProp))
if __name__ == "__main__":
main()
donne
Traceback (most recent call last):
File "prop_qt.py", line 19, in <module>
main()
File "prop_qt.py", line 15, in main
print("Qt object property: {}".format(qc.myProp))
AttributeError: 'MyQtClass' object has no attribute 'myProp'
Mais la MyQtClass
classe ne ont une propriété myProp
, il contient juste un bug! Cela m'a pris un moment pour déboguer dans une application réelle. Donc, ma question est la suivante: que se passe-t-il ici? Est-ce un bug dans PyQt? Ou est-ce que je fais quelque chose de mal?
EDIT:
réponse de Ekhumoro m'a incité à regarder dans la source PyQt (5,6). Il semble que l'erreur provient de QtCore/qpycore_qobject_getattr.cpp
, qui définit la fonction suivante
// See if we can find an attribute in the Qt meta-type system. This is
// primarily to support access to JavaScript (e.g. QQuickItem) so we don't
// support overloads.
PyObject *qpycore_qobject_getattr(const QObject *qobj, PyObject *py_qobj,
const char *name)
{
const QMetaObject *mo = qobj->metaObject();
// Try and find a method with the name.
QMetaMethod method;
int method_index = -1;
// Count down to allow overrides (assuming they are possible).
for (int m = mo->methodCount() - 1; m >= 0; --m)
{
method = mo->method(m);
if (method.methodType() == QMetaMethod::Constructor)
continue;
// Get the method name.
QByteArray mname(method.methodSignature());
int idx = mname.indexOf('(');
if (idx >= 0)
mname.truncate(idx);
if (mname == name)
{
method_index = m;
break;
}
}
if (method_index >= 0)
{
// Get the value to return. Note that this is recreated each time. We
// could put a descriptor in the type dictionary to satisfy the request
// in future but the typical use case is getting a value from a C++
// proxy (e.g. QDeclarativeItem) and we can't assume that what is being
// proxied is the same each time.
if (method.methodType() == QMetaMethod::Signal)
{
// We need to keep explicit references to the unbound signals
// (because we don't use the type dictionary to do so) because they
// own the parsed signature which may be needed by a PyQtSlotProxy
// at some point.
typedef QHash<QByteArray, PyObject *> SignalHash;
static SignalHash *sig_hash = 0;
// For crappy compilers.
if (!sig_hash)
sig_hash = new SignalHash;
PyObject *sig_obj;
QByteArray sig_str = method.methodSignature();
SignalHash::const_iterator it = sig_hash->find(sig_str);
if (it == sig_hash->end())
{
sig_obj = (PyObject *)qpycore_pyqtSignal_New(
sig_str.constData());
if (!sig_obj)
return 0;
sig_hash->insert(sig_str, sig_obj);
}
else
{
sig_obj = it.value();
}
return qpycore_pyqtBoundSignal_New((qpycore_pyqtSignal *)sig_obj,
py_qobj, const_cast<QObject *>(qobj));
}
// Respect the 'private' nature of __ names.
if (name[0] != '_' || name[1] != '_')
{
QByteArray py_name(Py_TYPE(py_qobj)->tp_name);
py_name.append('.');
py_name.append(name);
return qpycore_pyqtMethodProxy_New(const_cast<QObject *>(qobj),
method_index, py_name);
}
}
// Replicate the standard Python exception.
PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '%s'",
Py_TYPE(py_qobj)->tp_name, name);
return 0;
}
Si aucune méthode de ce nom se trouve dans le système de méta type Qt, ce message d'erreur est soulevée. Je suppose qu'il serait difficile de faire autre chose en effet.
Je suis curieux de savoir pourquoi vous n'avez pas répondu à la réponse que j'ai donnée. Êtes-vous en désaccord avec le fait que cela touche tous les points de votre question? – ekhumoro
Ouais. J'étais occupé et pouvais continuer mon travail sans connaître la réponse, alors ça m'a échappé. Merci d'avoir suivi. – titusjan