'Unhashable type in python
I am trying to call functions of a C based .dll from python, by creating wrappers.I face a scenario where i need to create a callback in python for a function in that .C dll. In C, The callback function is of type
typedef void (*Log_Callback_t)(void * pDataParams, uint8_t bOption, Log_Entry* pLogEntries, uint16_t wEntryCount);
src.py
# I am adding excerpts of the .py file. Pls indicate if it's incomplete or unclear
class Log_Entry(Structure):
_pack_ = 1
_fields_ = [('bLogType',c_ubyte),
('wDataLen',c_uint16)
]
logArray = (Log_Entry)()
def LogCallBack(pDataParams,bOption,pLogEntries,wEntryCount):
GlobalVars.log.info("In LogCallBack")
# Creating function ptr type
callback_type = CFUNCTYPE(None,c_void_p,c_ubyte,pointer(LogEntry),c_uint16)
# Creating function ptr of LogCallBack
callback_ptr = callback_type(LogCallBack)
# Passing the created function ptr to python wrapper
status1 = LogInit (callback_ptr,32)
While running this, i face the below issue with pointer(Log_Entry) i suppose. Can someone pls point me where am i going wrong
callback_type = CFUNCTYPE(None,c_void_p,c_ubyte,pointer(LogEntry),c_uint16)
File "C:\python\lib\ctypes\__init__.py", line 99, in CFUNCTYPE
return _c_functype_cache[(restype, argtypes, flags)]
TypeError: unhashable type
Exception TypeError: unhashable type
I referred to already posted questions related to unhashable type, but i did not find related to it, hence posting here. BTW, i tried with byref as well.
Solution 1:[1]
pointer(LogEntry) is an instance of a pointer to an instance of a variable called LogEntry that isn't defined.
POINTER(type) is the type of a pointer to the specified type. Types are needed by .argtypes, so POINTER(Log_Entry) is needed.
Working example:
test.c
#include <stdint.h>
#define API __declspec(dllexport)
#pragma pack(push,1)
typedef struct Log_Entry {
uint8_t bLogType;
uint16_t wDataLen;
} Log_Entry;
#pragma pack(pop)
typedef void (*Log_Callback_t)(void * pDataParams, uint8_t bOption,
Log_Entry* pLogEntries, uint16_t wEntryCount);
API int LogInit(Log_Callback_t cb, uint8_t num) {
Log_Entry entry[2];
entry[0].bLogType = 1;
entry[0].wDataLen = 2;
entry[1].bLogType = 3;
entry[1].wDataLen = 4;
cb(NULL, num, entry, 2);
return 6;
}
test.py
from ctypes import *
class Log_Entry(Structure):
_pack_ = 1
_fields_ = [('bLogType',c_ubyte),
('wDataLen',c_uint16)]
def __repr__(self):
return f'Log_Entry(bLogType={self.bLogType}, wDataLen={self.wDataLen})'
CALLBACK = CFUNCTYPE(None, c_void_p, c_ubyte, POINTER(Log_Entry), c_uint8)
dll = CDLL('./test')
LogInit = dll.LogInit
LogInit.argtypes = CALLBACK, c_uint16
LogInit.restype = c_int
@CALLBACK
def LogCallBack(pDataParams, bOption, pLogEntries, wEntryCount):
print(f'{pDataParams=} {bOption=} {wEntryCount=}')
for i in range(wEntryCount):
print(f'LogEntry[{i}]={pLogEntries[i]}')
status1 = LogInit(LogCallBack, 32)
Output:
pDataParams=None bOption=32 wEntryCount=2
LogEntry[0]=Log_Entry(bLogType=1, wDataLen=2)
LogEntry[1]=Log_Entry(bLogType=3, wDataLen=4)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Mark Tolonen |
