Listing [Python.Docs]: ctypes - A foreign function library for Python.
In order for everything to be properly converted (Python <=> C) when calling the function (residing in a .dll (.so)), 2 things need to be specified (leaving x86 (pc032) calling convention (Win) aside):
Argument types
Return type
In CTypes, this is achieved by specifying:
argtypes - a sequence (tuple, list) containing each function's argument (CTypes) type, in the order they appear in the function declaration (if the function only has one argument, there should be one element sequence)
restype - a single CTypes type (function's return type)
Side note: an alternative to the above, is prototyping foreign functions (CFUNCTYPE, WINFUNCTYPE, PYFUNCTYPE - check the Function prototypes section (in the URL at the beginning)).
Anyway:
any of them (where required (1)), will result in defaults being applied: all are treated (C89 style) as ints, which (on most systems) are 32 bits long.
This generates Undefined Behavior (2) (also applies when incorrectly specifying them), especially on 064bit CPU / OS (and Python process), where values of larger types (e.g. pointers) could be truncated (check [SO]: Maximum and minimum value of C types integers from Python (@CristiFati's answer) for details (limits -> size) regarding C integral types).
The displayed errors can be numerous and sometimes misleading.
You misspelled argtype (lacking an s at the end).
Correct that, and you should be fine
Example:
For a function func exported by libdll.dll (libdll.so) with the following header:
double func(uint32_t ui32,
float f,
long long vll[8],
void *pv,
char *pc,
uint8_t *pui8);
The Python equivalent would be:
import ctypes as cts
func = libdll.func
func.argtypes = (cts.c_uint32,
cts.c_float,
cts.c_longlong * 8,
cts.c_void_p,
cts.c_char_p,
cts.POINTER(cts.c_ubyte))
'''
Last 2 argument types are almost the same (1st is signed while 2nd is unsigned).
- 1st is used for NUL terminated strings (that work with strcpy, strlen, ...).
Python automatically converts them to bytes
- 2nd is used for buffers in general (which may contain NUL (0x00) characters)
'''
func.restype = cts.c_double
Some (more destructive) outcomes of the same (or very similar) scenario (there are many others):
Footnotes
#1: Technically, there are situations where it's not required to specify them. But even then, it's best to have them specified in order to eliminate any possible confusion:
Function with no arguments:
function_from_dll.argtypes = ()
Function returning void:
function_from_dll.restype = None
#2: Undefined Behavior ([Wikipedia]: Undefined behavior) as the name suggests, is a situation when the outcome of a piece of code can't be "predicted" (or guaranteed by the C (C++) standard). The main cases:
Works as expected
Doesn't work as expected
The "beauty" of it is that sometimes it seems totally random, sometimes it "only reproduces" under some specific circumstances (different machines, different OSes, different environments, ...). Bottom line is that all of them are purely coincidental! The problem lies in the code (can be the current code (highest chances) or other code it uses (libraries, compilers)).