I'm trying to use PyQt5's DBus module to interact with the KDE PowerManagerAgent. When calling the AddInhibition method I need to send the first paramter as an uint32 (Unsigned int), but the code sends the value as a singed int.
The code is written using Python 3
self.dBus = QtDBus.QDBusConnection.sessionBus()
msg = QtDBus.QDBusMessage.createMethodCall(self.dBusService, self.dBusPath,self.dBusInterface,'AddInhibition')
msg << 1 << who << reason
reply = QtDBus.QDBusReply(self.dBus.call(msg))
Looking at the output from dbus-monitor I can tell that the code does indeed contact the powermonitor but fails to find the correct AddInhibition method due to the first parameter being type as int32
Output from dbus-monitor when trying to call AddInhibition
Call
method call time=1549706946.073218 sender=:1.172 -> destination=org.kde.Solid.PowerManagement.PolicyAgent serial=5 path=/org/kde/Solid/PowerManagement/PolicyAgent; interface=org.kde.Solid.PowerManagement.PolicyAgent; member=AddInhibition
int32 1
string "This"
string "fails"
Reply
error time=1549706946.073536 sender=:1.29 -> destination=:1.172 error_name=org.freedesktop.DBus.Error.UnknownMethod reply_serial=5
string "No such method 'AddInhibition' in interface 'org.kde.Solid.PowerManagement.PolicyAgent' at object path '/org/kde/Solid/PowerManagement/PolicyAgent' (signature 'iss')"
Output from dbus-monitor when using QDBusViewer application
Call
method call time=1549723045.320128 sender=:1.82 -> destination=org.kde.Solid.PowerManagement.PolicyAgent serial=177 path=/org/kde/Solid/PowerManagement/PolicyAgent; interface=org.kde.Solid.PowerManagement.PolicyAgent; member=AddInhibition
uint32 1
string "This"
string "Works"
Reply
method return time=1549723045.320888 sender=:1.29 -> destination=:1.82 serial=1370 reply_serial=177
uint32 30
Since Python is not strongly typed how do I specify the the parameter must be typed as an unsigned int?
You can use the DBusArgument class to do this by specifying the QMetaType of the argument.
For example, say you want to use the RequestName method from org.freedesktop.DBus (see the spec). The flags argument is an unsigned int, so you'll run into this problem:
>>> from PyQt5.QtDBus import QDBusConnection, QDBusInterface
>>> sessionbus = QDBusConnection.sessionBus()
>>> iface = QDBusInterface("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", sessionbus)
>>> c = iface.call('RequestName', 'com.mydomain.myapp', 4)
>>> c.arguments()
['Call to RequestName has wrong args (si, expected su)\n']
So, it's saying it got a string and an integer (si), but it wanted a string and an unsigned integer (su). So, we'll use the QDBusArgument class and specify QMetaType.UInt:
>>> from PyQt5.QtCore import QMetaType
>>> from PyQt5.QtDBus import QDBusConnection, QDBusInterface, QDBusArgument
>>> sessionbus = QDBusConnection.sessionBus()
>>> iface = QDBusInterface("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", sessionbus)
>>> a1 = QDBusArgument()
>>> a1.add('com.mydomain.myapp', QMetaType.QString)
>>> a2 = QDBusArgument(4, QMetaType.UInt)
>>> c = iface.call('RequestName', a1, a2)
>>> c.arguments()
[1]
Since the string was fine, that didn't have to be a QDBusArgument. I just wanted to show the two ways of constructing it (with the .add() method and just using the constructor).
Related
I am writing a wrapper for a C library in Python. I am trying to properly annotate all of the methods, so my IDE can help me catch errors. I am stuck annotating one method, can you help me figure out the proper annotation?
One of the methods in the C library works as follows:
Takes one arg: pointer to a character buffer
Buffer is made via: char_buffer = ctypes.create_string_buffer(16)
Populates the char buffer with the output value
Done via CMethod(char_buffer)
One then parses the buffer by doing something like char_buffer.value.
How can I annotate the wrapper method to look for a pointer to a character buffer? Currently, I have the below, but I think this is incorrect, since POINTER seems to be just a function in _ctypes.py.
from ctypes import POINTER
def wrapped_method(char_buffer: POINTER):
CMethod(char_buffer)
According to [Python.Docs]: ctypes.create_string_buffer(init_or_size, size=None):
This function creates a mutable character buffer. The returned object is a ctypes array of c_char.
Example:
>>> import ctypes
>>>
>>> CharArr16 = ctypes.c_char * 16
>>> s = ctypes.create_string_buffer(16)
>>>
>>> isinstance(s, CharArr16)
True
>>> isinstance(s, ctypes.c_char * 15)
False
>>> isinstance(s, ctypes.c_char * 17)
False
>>>
>>> # A more general form, but it WILL FAIL for non array instances
...
>>> isinstance(s, s._type_ * s._length_)
True
>>>
>>> # A more general form that WILL WORK
...
>>> issubclass(CharArr16, ctypes.Array)
True
>>> isinstance(s, ctypes.Array)
True
i'm trying to write a Python DLL Wrapper for a C Project
https://github.com/OpenEtherCATsociety/SOEM -> C Project
The Python DLL Wrapper can be found here:
https://github.com/GitHubStefan13/SOEM-for-Python
Original C Code that is important:
char IOmap[4096];
int ec_config_overlap(uint8 usetable, void *pIOmap);
Python Wrapper
IOMap = ctypes.POINTER(ctypes.c_char * 4096)
c_ec_config_overlap = ethercat.ec_config_overlap
c_ec_config_overlap.argtypes = [ctypes.c_unit8, IOMap]
c_ec_config_overlap.restype = ctypes.c_int
When im trying to define a function in Python
def ec_config_overlap(usetable, PIOMap):
return c_ec_config_overlap(usetable, PIOMap
and call it.
I receive the error
ctypes.ArgumentError: argument 2: : expected LPc_char_Array_4096 instance instead of _ctypes.PyPointerType.
I understand the Error but how do i go around to actually make the ctype a Array[4096] instead of the PyPointerType?
This syntax creates array instances:
>>> import ctypes
>>> (ctypes.c_char*4096)()
<__main__.c_char_Array_4096 object at 0x0000024D84E2D7C8>
Since it is a char array, you can also use:
>>> create_string_buffer(4096)
<__main__.c_char_Array_4096 object at 0x0000025AE48FE948>
The type of your function should be:
c_ec_config_overlap.argtypes = [ctypes.c_uint8, ctypes.c_void_p]
But for better type checking you can also use:
c_ec_config_overlap.argtypes = [ctypes.c_uint8, ctypes.c_char_p]
I am working on a C++ Dll with C wrapper and I am creating a Python wrapper for future user (I discover ctypes since monday). One of the method of my Dll (because it is a class) return an unsigned short **, call data, which corresponds to an image. On C++, I get the value of a pixel using data[row][column].
I create in Python a function on the following model :
mydll.cMyFunction.argtypes = [c_void_p]
mydll.cMyFunction.restype = POINTER(POINTER(c_ushort))
When I call this function, I have result = <__main__.LP_LP_c_ushort at 0x577fac8>
and when I try to see the data at this address (using result.contents.contents) I get the correct value of the first pixel. But I don't know how to access values of the rest of my image. Is there a easy way to do something like C++ (data[i][j]) ?
Yes, just use result[i][j]. Here's a contrived example:
>>> from ctypes import *
>>> ppus = POINTER(POINTER(c_ushort))
>>> ppus
<class '__main__.LP_LP_c_ushort'>
>>> # This creates an array of pointers to ushort[5] arrays
>>> x=(POINTER(c_ushort)*5)(*[cast((c_ushort*5)(n,n+1,n+2,n+3,n+4),POINTER(c_ushort)) for n in range(0,25,5)])
>>> a = cast(x,ppus) # gets a ushort**
>>> a
<__main__.LP_LP_c_ushort object at 0x00000000026F39C8>
>>> a[0] # deref to get the first ushort[5] array
<__main__.LP_c_ushort object at 0x00000000026F33C8>
>>> a[0][0] # get an item from a row
0
>>> a[0][1]
1
>>>
>>> a[1][0]
5
So if you are returning the ushort** correctly from C, it should "just work".
Which data type should be used in this stringUpcase function in my DLL file
void __cdecl stringUpcase(char IN_str[], char OUT_str[], int32_t len);
I am trying ctype.c_char_p in Python 3.6 and This function should return the uppercase string "HELO" in OUT_str variable.
dl = ctypes.cdll.LoadLibrary("path/of/dll/file.dll")
IN_str = 'helo'
OUT_str = ''
IN_len = len(IN_str)
dl.stringUpcase.restype = ctypes.c_void_p
dl.stringUpcase.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int32]
dl.stringUpcase(IN_str, OUT_str, IN_len);
Console error output is
line 21, in <module>
dl.stringUpcase(IN_str, OUT_str, IN_len);
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type
Thank you for any help you can provide.
You are trying to pass python string as an argument of type c_char_p. Python3 unlike python2 is pretty strict about typing in ctypes.
The working sample would look like this.
import ctypes
ins = ctypes.c_char_p(b'helo')
lens = len(ins.value)
outs = ctypes.create_string_buffer(lens+1)
lib = ctypes.cdll.LoadLibrary("./upper.so")
lib.stringUpcase.restype = None
lib.stringUpcase.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int32]
lib.stringUpcase(ins, outs, lens)
print(outs.value)
Pay attention to the fact that c_char_p points to a zero-terminated string, so len argument is redundant here. Moreover to construct c_char_p you need to pass bytes object or and integer address rather than just a string, to be able to use string as an argument you need to use c_wchar_p type and wchar_t* in your library respectively.
One more thing to pay attention to is the fact that your C function does not allocate memory so you need for outs to be large enough to contain the result.
I've got some C code, which I'm trying to access from ctypes in python. A particular function looks something like this:
float *foo(void) {
static float bar[2];
// Populate bar
return bar;
}
I know this isn't an ideal way to write C, but it does the job in this case. I'm struggling to write the python to get the two floats contained in the response. I'm fine with return values that are single variables, but I can't work out from the ctypes docs how to handle a pointer to an array.
Any ideas?
Specify restypes as [POINTER][1](c_float):
import ctypes
libfoo = ctypes.cdll.LoadLibrary('./foo.so')
foo = libfoo.foo
foo.argtypes = ()
foo.restype = ctypes.POINTER(ctypes.c_float)
result = foo()
print(result[0], result[1])
Thanks to #falsetru, believe I found a somehow better solution, which takes into account the fact that the C function returns a pointer to two floats:
import ctypes
libfoo = ctypes.cdll.LoadLibrary('./foo.so')
foo = libfoo.foo
foo.argtypes = ()
foo.restype = ctypes.POINTER(ctypes.c_float * 2)
result = foo().contents
print('length of the returned list: ' + len(result))
print(result[0], result[1])