Skip to content

Instantly share code, notes, and snippets.

@DDR0
Last active February 16, 2019 05:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DDR0/a80350a2465f63930221dc7642e59067 to your computer and use it in GitHub Desktop.
Save DDR0/a80350a2465f63930221dc7642e59067 to your computer and use it in GitHub Desktop.
I believe the PyQt5 documentation is in error. This is why:

Qt Dbus Error Emitting Issue

Given a function emitError, as defined at https://github.com/krontech/chronos-gui-2/blob/be81cf2ae25e74d407d47f4eebe9a11f62524d71/src/coordinator_api.py#L751,

def emitError(self, message: str) -> None:
	error = QDBusMessage.createError(QDBusError.Failed, message)
	QDBusConnection.systemBus().send(error)

My problem is that this should work but does not. I have good reason to think it should work. Checking the documentation, https://doc.qt.io/archives/qt-5.7/qdbuserror.html writes:

QDBusError objects are used to inspect the error name and message as received from the bus and remote applications. You should not create such objects yourself to signal error conditions when called from D-Bus: instead, use QDBusMessage::createError() and QDBusConnection::send().

OK, we're not supposed to create a qdbuserror when we want a qt dbus error. We need to create the error and then send it. Sounds reasonable. Let's look up the functions recommended:

https://doc.qt.io/archives/qt-5.7/qdbusconnection.html#send says:

bool QDBusConnection::send(const QDBusMessage &message) const

Sends the message over this connection, without waiting for a reply. This is suitable for errors, signals, and return values as well as calls whose return values are not necessary.

Returns true if the message was queued successfully, false otherwise.

That's straight-forward. We take the output of QDBusMessage::createError() and feed it to QDBusConnection::send().

So, how do we create an error? https://doc.qt.io/archives/qt-5.7/qdbusmessage.html#createError gives us three options:

  1. create error with string name and string message: Constructs a new DBus message representing an error, with the given name and msg.

  2. create error from QDBusError Constructs a new DBus message representing the given error.

  3. create error with QDBusError::ErrorType (int) and string message: Constructs a new DBus message for the error type type using the message msg. Returns the DBus message.

This is a list ranging somewhere between filthy lies and useless information.

  1. Sending an error constructed like this results in the message,

    	QDBusConnection: error: could not send error message to service "": Invalid error name: failed
    

    The code in question is:

    	def emitError(self, message: str) -> None:
    		error = QDBusMessage.createError('failed', message)
    		QDBusConnection.systemBus().send(error)

    so this is an indirect lie. The documentation places no limit on what error name may be, and the name cannot be guessed.

  2. Since the documentation listed above for QDBusError says not to create these manually, this method is useless in our case. Literally, the documentation reads, "You should not create such objects yourself" to do what we're trying to do.

  3. Sending an error constructed like this results in the message,

    	process 2061: arguments to dbus_message_get_sender() were incorrect, assertion "message != NULL" failed in file ../../dbus/dbus-message.c line 3514.
    	This is normally a bug in some application using the D-Bus library.
    

    The code in question is:

    	def emitError(self, message: str) -> None:
    		error = QDBusMessage.createError(QDBusError.Failed, message)
    		QDBusConnection.systemBus().send(error)

    so in this case the documentation has directly lied, since we have followed it to the letter and yet have emitted no error as the documentation states.

I find the issue unlikely to lay in the translation from C++ to Python. In Python, QDBusMessage::createError ought to be QDBusMessage.createError and QDBusConnection::send QDBusConnection.systemBus().send(). I chose to add systemBus() in the second translation, since while you can use QDBusConnection.send() directly, you have to pass it a connection as its first argument - in this case, I assume QDBusConnection.systemBus(). I assume the two are equivalent since I get the same error message out of the call either way. In the end, I concluded I was to use QDBusMessage.createSignal to create a signal to send with QDBusConnection.systemBus().send().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment