Using PyQt5's D-Bus, how do I create an error? For example, if someone sends an unknown command in a chat program, I'd like to be able to let the client know something like as follows:
error = QDBusMessage.createError(QDBusError.UnknownProperty, "unknown command")
QDBusConnection.systemBus().send(error)
However, this fails with the message:
*** TypeError: arguments did not match any overloaded call:
createErrorReply(self, str, str): first argument of unbound method must have type 'QDBusMessage'
createErrorReply(self, QDBusError): first argument of unbound method must have type 'QDBusMessage'
createErrorReply(self, QDBusError.ErrorType, str): first argument of unbound method must have type 'QDBusMessage'
I can't make heads no tail of this error, since as far as I can tell I'm using it exactly as described. The first arg must be QDBusMessage, since that's what's before the dot. The second arg is of the right type, being <class 'PyQt5.QtDBus.QDBusError.ErrorType'> as returned by type(QDBusError.UnknownProperty). And the quotes mean it's a string, which is what str is.
I also tried sendErrorReply(), but that doesn't seem to exist in PyQt5. At least, I can't find it - it's not beside systemBus's send(), and it's not found in QDBusMessage.
Neither of the examples in PyQt5's examples/dbus/chat folder emit errors. There is no Python documentation available at http://pyqt.sourceforge.net/Docs/PyQt5/QtDBus.html.
I came across your question when I had the same problem as you, a lack of sendErrorReply in PyQt5.
Here's how I got it to work in my Bluetooth application:
#pyqtSlot(QDBusMessage)
def AuthorizeService(self, message):
print("rejecting service authorization")
error = message.createErrorReply(QDBusError.AccessDenied, "Failed")
self._bus.send(error)
The trick is to create the error reply from the message that was received.
Related
I want to use assertRaises and check for the error message too. According to the docs I have to use it as a context manager.
with self.assertRaises(ValueError, msg='Invalid id format.'):
api.get_by_id('a')
This results in the following error:
TypeError: assertRaises() missing 1 required positional argument: 'callableObj'
I get the exact same error if I use it without msg.
Using it like assertRaises(exception, callable, *args, **kwds) works fine, but that obviously can't process the error message.
I don't understand why Python can't recognise the use case I'm going for.
Python 3.7.10, MacOS Monterey 12.2
Two things - first of all, it's hard to tell what actually happens but there must be some other error in your code because
with self.assertRaises(ValueError, msg='Invalid id format.'): should work perfectly fine (tested on Python 3.10)
2nd thing - msg argument does not do what you want it to do - self.assertRaises as a context manager with msg argument not working as expected
Link provided also explains how to check for error message with assertRaisesRegex
I'm writing an MPRIS player, which communicates with clients over
dbus. I need to emit a signal when my playback state changes. However,
the signal requires a format of (sa{sv}as), and my code is producing
(sa{sv}av). Here's the important part:
self.signal = QDBusMessage.createSignal(
"/org/mpris/MediaPlayer2",
"org.freedesktop.DBus.Properties",
"PropertiesChanged"
)
self.signal.setArguments(
[interface, {property: values}, ['']]
)
The problem is the third item in the list given to setArguments. It is
an empty string in a list because I need to produce a type of 'array
of string' (as) but pyqt5 translates that into 'array of variant' (av).
I never need to put any actual data in that list, I just need the type
signature to be correct.
Is there any way to do this in PyQt5? Perhaps using QDBusArgument?
I... I got it working. Wow. That was a campaign.
I don't know what is going wrong exactly, I wasn't able to dig out of the PyQt5 source where exactly the conversion happens. However, I did look into QDbusArgument(). There is no documentation for this in python, and the C++ docs are worthless due to major differences, so I took to the source code. in sip/QtDbus/qdbusargument.sip, I discovered a completely undocumented new method called qdbusargument_add. This maps to QDbusArgument().add() in python. It is used to add arguments with an explicit type id to a QDbusArgument. And it has a special case for QStringList!
From then I just bruteforced every possibility I could think of with arguments to QDbusArgument(), and finally got the following:
def PropertiesChanged(self, interface, property, values):
"""Sends PropertiesChanged signal through sessionBus.
Args:
interface: interface name
property: property name
values: current property value(s)
"""
emptyStringListArg = QDBusArgument()
emptyStringListArg.add([""], QMetaType.QStringList)
self.signal.setArguments([interface, {property: values}, emptyStringListArg])
QDBusConnection.sessionBus().send(self.signal)
It'd be great if the add() function could be documented, but I can't seem to send messages to the PyQt5 mailing list. Do I have to register first?
I'd like http://example.com/view_item/4 to handle a view for a selected object by passing the object ID, but after trying every combination I've found online of what will work, I'm still getting the following error:
TypeError: view must be a callable or a list/tuple in the case of include().
This is my path:
url(r'^view_item/(?P<query>\w+)$','view_item')]
I've verified the regex, I've tried without the regex.
I was forgetting, as the error stated, to include the callable view. My confusion comes from mixed methods in learning resources.
Working path example:
url(r'^view_item/(?P<query>\w+)$',view_item, 'view_item')]
I am using a program where Python is the native scripting language. Unfortunately, they have a native function that uses the name bytes. This causes a problem when I am trying to use the actual bytes built-in function, and it thinks I am referencing that built-in variable. I will show you what I mean, one object as the following built-in code:
def receive(row, table, message, bytes):
#This is defined in the GUI
So, row, table, message, and bytes are all passed in as arguments, effectively overwriting the name bytes. So if I were to say bytes(something).decode() I get a TypeError: 'bytes' object is not callable
Is there any way to get out of this jam?
Use a different name for the fourth parameter (if you can change the signature of the function)
def receive(row, table, message, bytes_):
#This is defined in the GUI
Your problem is similar to this one. Just from builtins import bytes as _bytes; this will let you do _bytes(something).decode().
Although renaming the fourth argument is a better solution.
I have a BaseHandler class that subclasses the Tipfy RequestHandler in my AppEngine site. In it, I have setup a "poor man's" browser sniffer for mobile devices with a class attribute (a tuple) containing device names.
In a subsequent method, I loop through the device names in the tuple and check them against the user agent string from the Request object. If I get a match, I set an instance attribute called "is_mobile" to True.
In that method, however, Python is giving me a "TypeError: argument of type 'UserAgent' is not iterable" error, and I can't understand why, since the line it is complaining about is not (as far as I understand) a loop.
Here is the code:
class BaseHandler(RequestHandler, AppEngineAuthMixin, AllSessionMixins):
mobile_devices = ('Android', 'iPhone', 'iPod', 'Blackberry')
....
def detect_mobile_devices(self):
found_device = False
for device in self.__class__.mobile_devices:
if device in self.request.user_agent:
found_device = True
break
self.is_mobile = found_device
Here is the line Python does not like:
File "/path/to/project/app/apps/remember_things/handlers.py", line 56, in detect_mobile_devices
if device in self.request.user_agent:
The expression
device in self.request.user_agent
will first try to call
self.request.user_agent.__contains__(device)
If this method does not exist, Python tries to iterate over self.request.user_agent and compares each item it encounters to device. Obviously, the type of self.request.user_agent neither allows .__contains__() nor iteration, hence the error message.
Also see the documentation of membership test in Python.