PyQt5 create error upon failed dbus request - python

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

Getting Python assertRaises to check for message

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

PyQt5 dbus: Force type signature of signal argument to be array of string

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?

Django accepting QueryParams (Must be callable error)

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')]

Namespacing issue in Python

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.

Why would Python give me a "TypeError: argument of type 'UserAgent' is not iterable" in a non-iteration type of operation?

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.

Categories