# HG changeset patch # User Tassilo Philipp # Date 1659533887 -7200 # Node ID 8e905c0798c70944ec6d624bc096c56451cd91ce # Parent 2725de59454a43d24fcabbb3d014844d63cf72b6 - p2Z() helper func - import fix for test code avoiding potential circular import diff -r 2725de59454a -r 8e905c0798c7 python/pydc/README.txt --- a/python/pydc/README.txt Tue Jun 22 18:49:02 2021 +0200 +++ b/python/pydc/README.txt Wed Aug 03 15:38:07 2022 +0200 @@ -1,6 +1,6 @@ dyncall python bindings Copyright 2007-2016 Daniel Adler - 2018-2020 Tassilo Philipp + 2018-2022 Tassilo Philipp Dec 4, 2007: initial Mar 22, 2016: update to dyncall 0.9, includes breaking sig char changes @@ -18,6 +18,12 @@ Jan 23, 2021: update to dyncall 1.2 (only version bump) Feb 2, 2021: added callback support (comes with some bigger refactoring); allow CPython's Py{CObject,Capsule} to be used as 'p'ointers +Aug 3, 2022: added p2Z() helper function (e.g. helpful when working with + string 'p'ointers that eventually need to be free()'d, as no + implicit 'Z' conversion is taking place) + + + BUILD/INSTALLATION @@ -115,6 +121,7 @@ ----+----------------------------------------------------------------------------------------------------------- '_' | prefix indicating that next signature character specifies calling convention; char is one of the following ':' | platform's default calling convention + '*' | platform's default thiscall (C++ methods) calling convention, first argument of a call is the this pointer 'e' | vararg function '.' | vararg function's variadic/ellipsis part (...), to be specified before first vararg 'c' | only on x86: cdecl @@ -136,6 +143,7 @@ - stub location: the pydc-stubs folder isn't picked up by mypy, so I question why this is the suggested way - get into freebsd ports - releasing GIL when calling into C code (*only* when none of the arguments is a mutable buffer, though) +- support for new dyncall aggregate-by-value interface BUGS diff -r 2725de59454a -r 8e905c0798c7 python/pydc/pydc.c --- a/python/pydc/pydc.c Tue Jun 22 18:49:02 2021 +0200 +++ b/python/pydc/pydc.c Wed Aug 03 15:38:07 2022 +0200 @@ -733,6 +733,20 @@ } +/* helper creating a string from a pointer handle (Py_ssize_t, must point to + * string data); this makes it easier to use C functions that allocate memory, + * retrieve the handle via 'p' in order to call free() on it later, and get the + * string it points to + */ +static PyObject* +pydc_p2Z(PyObject* self, PyObject* args) +{ + size_t p; + if(PyArg_ParseTuple(args, "n", &p)) + return Py_BuildValue("s", (const char*)p); + + Py_RETURN_NONE; +} // module deinit @@ -772,6 +786,7 @@ {"call", pydc_call, METH_VARARGS, "call function" }, {"new_callback", pydc_new_callback, METH_VARARGS, "new callback obj" }, // @@@ doc: only functions, not every callable, and only with positional args {"free_callback", pydc_free_callback, METH_VARARGS, "free callback obj"}, + {"p2Z", pydc_p2Z, METH_VARARGS, "ptr to C-string" }, // helper func {NULL,NULL,0,NULL} }; diff -r 2725de59454a -r 8e905c0798c7 python/pydc/pydc.pyi --- a/python/pydc/pydc.pyi Tue Jun 22 18:49:02 2021 +0200 +++ b/python/pydc/pydc.pyi Wed Aug 03 15:38:07 2022 +0200 @@ -12,4 +12,5 @@ def call(funcptr: H, signature: str, *arguments: Any) -> Any: ... def new_callback(c_signature: str, f: Callable) -> H: ... # note: only callbacks with positional args def free_callback(cbhandle: H) -> None: ... +def p2Z(ptr: int) -> Optional[str]: ... # note: only correct for python 3 diff -r 2725de59454a -r 8e905c0798c7 python/pydc/setup.py --- a/python/pydc/setup.py Tue Jun 22 18:49:02 2021 +0200 +++ b/python/pydc/setup.py Wed Aug 03 15:38:07 2022 +0200 @@ -7,7 +7,7 @@ setup( name = 'pydc' -, version = '1.2.5' +, version = '1.2.6' , author = 'Daniel Adler, Tassilo Philipp' , author_email = 'dadler@dyncall.org, tphilip@dyncall.org' , maintainer = 'Daniel Adler, Tassilo Philipp' diff -r 2725de59454a -r 8e905c0798c7 python/pydc/test/Makefile --- a/python/pydc/test/Makefile Tue Jun 22 18:49:02 2021 +0200 +++ b/python/pydc/test/Makefile Wed Aug 03 15:38:07 2022 +0200 @@ -5,5 +5,5 @@ echo "$$i `echo $$i | sed -E 's/\*/ p/;s/(^| )([a-z])[^ ]*/\2/g'`_plus_one($$i v) { return v+1; }"; \ done; \ echo 'void cp_head_incr(char* v) { v[0]+=1; }') | ${CC} -`[ \`uname\` = Darwin ] && echo dynamiclib || echo shared` -x c - -o $@ - @echo Done! Now you can run types.py + @echo Done! Now you can run types_test.py diff -r 2725de59454a -r 8e905c0798c7 python/pydc/test/types.py --- a/python/pydc/test/types.py Tue Jun 22 18:49:02 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,193 +0,0 @@ -import pydc -import sys -import platform -import copy -import types - - - -def theader(title): - print("\n"+title) - print('%8s %20s %16s %-20s %11s %-16s -> %16s %-16s %12s %-16s # %s' % ('DC_SIG', 'C_RET_T', 'C_FSYM', 'C_PARAMLIST', 'PY_ARG_T', 'IN_ARGS', 'RET_VAL', 'PY_RET_T', 'OUT_ARGS', 'OUT_ARGS_T', 'NOTES')) - - -def t(lib, dcsig, c_rtype, c_fsym, c_paramlist, extra_msg, **kwargs): - args = kwargs['i'] if 'i' in kwargs else () - post_args = kwargs['p'] if 'p' in kwargs else copy.deepcopy(args) # expected args after call (as some can be modified in-place) - exp_ret = kwargs['r'] if 'r' in kwargs else None # expected return value - err_sgr = '' - # before call - inarg_types = '('+','.join(map(lambda x: type(x).__name__, args))+')' - inargs_str = ','.join(map(str, args)) - # call - try: - fp = pydc.find(lib, c_fsym) - r = pydc.call(fp, dcsig, *args) - rt = type(r).__name__ - if(r != exp_ret or args != post_args): # @@@ type test also or type(r) is not type(exp_ret)): - if((type(exp_ret) is not types.LambdaType) or exp_ret(r) == False): - err_sgr = '\033[41m' - except: - r = '[EXCEPTION]' - rt = '!' - if(exp_ret != Exception): - err_sgr = '\033[41m' - e = str(sys.exc_info()[1]) - extra_msg += ' "'+(e if len(e)<32 else e[0:32]+'...')+'"' - # after call - outarg_types = '('+','.join(map(lambda x: type(x).__name__, args))+')' - outargs = ','.join(map(str, args)) - print('%s%8s %20s %16s %-20s %11s \033[33m%-16s\033[39m -> \033[32m%16.16s\033[39m %-16s %12s \033[32m%-16s\033[0m # %s' % (err_sgr, dcsig, c_rtype, c_fsym, c_paramlist, inarg_types, inargs_str, str(r), '('+rt+')', outarg_types, outargs, extra_msg)) - - return r - - - -# some libc tests ------------------ - -try: - if len(sys.argv) > 1: - libc = pydc.load(sys.argv[1]) - elif sys.platform == "win32": - libc = pydc.load("msvcrt") - elif sys.platform == "darwin": - libc = pydc.load("/usr/lib/libc.dylib") - elif "bsd" in sys.platform: - libc = pydc.load("/usr/lib/libc.so") - #libc = pydc.load("/lib/libc.so.7") - elif platform.architecture()[0] == "64bit": - libc = pydc.load("/lib64/libc.so.6") - else: - libc = pydc.load("/lib/libc.so") - - theader('CLIB TESTS:') - - # void() - t(libc, ")v", "void", "sranddev", "(void)", '') - - # int() - t(libc, ")i", "int", "rand", "(void)", '', r=lambda i: type(i) is int) - - # void(unsigned int) - t(libc, "I)v", "void", "srand", "(int)", '', i=(123,)) - - # int() (and one different helper call for test) - x = \ - t(libc, ")i", "int", "rand", "(void)", 'with seed <------,', r=lambda i: type(i) is int) - t(libc, "I)v", "void", "srand", "(int)", 'set same seed |', i=(123,)) - t(libc, ")i", "int", "rand", "(void)", 'should be same result...', r=x) - t(libc, ")i", "int", "rand", "(void)", '...and now different result', r=lambda i: type(i) is int and i!=x) - - # int(int) - t(libc, "i)i", "int", "abs", "(int)", ' 10 => 10', i=( 10,), r=10) - t(libc, "i)i", "int", "abs", "(int)", ' 0 => 0', i=( 0,), r=0) - t(libc, "i)i", "int", "abs", "(int)", ' -9209 => 9209', i=(-9209,), r=9209) - - # long(long) - t(libc, "j)j", "long", "labs", "(long)", ' 48 => 48', i=( 48,), r=48) - t(libc, "j)j", "long", "labs", "(long)", ' 0 => 0', i=( 0,), r=0) - t(libc, "j)j", "long", "labs", "(long)", '-4271477497 => 4271477497', i=(-4271477497,), r=4271477497) - - # long long(long long) - t(libc, "l)l", "long long", "labs", "(long long)", ' 6334810198 => 6334810198', i=( 6334810198,), r=6334810198) - t(libc, "l)l", "long long", "labs", "(long long)", ' 1 => 1', i=( 1,), r=1) - t(libc, "l)l", "long long", "labs", "(long long)", ' 0 => 0', i=( 0,), r=0) - t(libc, "l)l", "long long", "labs", "(long long)", ' -1 => 1', i=( -1,), r=1) - t(libc, "l)l", "long long", "labs", "(long long)", '-7358758407 => 7358758407', i=(-7358758407,), r=7358758407) - - pydc.free(libc) -except: - print("\033[33mskipping clib tests because: "+str(sys.exc_info()[1])+"\033[0m\nnote: c-lib to use can be specified as command line param") - - -# tests with own .so for testing all conversions ------------------ - -l = pydc.load(sys.path[0]+"/test.so") - -# "long" typed test value use for Python 2 -long_i = 11234 -long_h = 0xdeadc0de -if sys.version_info < (3, 0): - long_i = long(11234) - long_h = long(0xdeadc0de) - -# test all possible arg types and their conversions to and from python, with -# specific focus/tests in areas where python 2 and 3 differ -theader('ARG & RET TYPE CONVERSION TESTS:') -t(l, "B)B", "int", "i_plus_one", "(int)", ' False => True (using int func in C)', i=(False,), r=True) - -t(l, "c)c", "char", "c_plus_one", "(char)", ' "a" (97) => 98', i=( 'a',), r=98) -t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' "a" (97) => 98', i=( 'a',), r=98) -t(l, "c)c", "char", "c_plus_one", "(char)", ' -2 => -1', i=( -2,), r=-1) -t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' 10 => 11', i=( 10,), r=11) - -t(l, "s)s", "short", "s_plus_one", "(short)", ' 10 => 11', i=( 10,), r=11) -t(l, "S)S", "unsigned short", "us_plus_one", "(unsigned short)", ' 10 => 11', i=( 10,), r=11) - -t(l, "i)i", "int", "i_plus_one", "(int)", ' 10 => 11', i=( 10,), r=11) -t(l, "I)I", "unsigned int", "ui_plus_one", "(unsigned int)", ' 10 => 11', i=( 10,), r=11) - -t(l, "j)j", "long", "l_plus_one", "(long)", ' 10 => 11', i=( 10,), r=11) -t(l, "J)J", "unsigned long", "ul_plus_one", "(unsigned long)", ' 10 => 11', i=( 10,), r=11) - -t(l, "l)l", "long long", "ll_plus_one", "(long long)", ' 10 => 11', i=( 10,), r=11) -t(l, "L)L", "unsigned long long", "ull_plus_one", "(unsigned long long)", ' 10 => 11', i=( 10,), r=11) -t(l, "l)l", "long long", "ll_plus_one", "(long long)", ' 11234 => 11235', i=(long_i,), r=11235) -t(l, "L)L", "unsigned long long", "ull_plus_one", "(unsigned long long)", ' 11234 => 11235', i=(long_i,), r=11235) - -t(l, "f)f", "float", "f_plus_one", "(float)", ' -1.23 => -0.23... (w/ fp imprecision)', i=( -1.23,), r=-0.23000001907348633) -t(l, "d)d", "double", "d_plus_one", "(double)", ' 5.67 => 6.67', i=( 5.67,), r= 6.67) - -t(l, "Z)Z", "const char*", "ccp_plus_one", "(const char*)", '"lose char" => "ose char"', i=( 'lose char',), r= 'ose char') # string object -t(l, "Z)Z", "const char*", "ccp_plus_one", "(const char*)", '"X_unicode" => "_unicode"', i=( u'X_unicode',), r=u'_unicode') # string object (unicode in Python 2) -t(l, "Z)Z", "const char*", "ccp_plus_one", "(const char*)", '"1lessbyte" => "lessbyte"', i=( b'1lessbyte',), r= 'lessbyte') # bytes object -t(l, "Z)Z", "const char*", "ccp_plus_one", "(const char*)", ' "xY" => "Y"', i=(bytearray(b'xY'),), r= 'Y') # bytearray object - -t(l, "p)Z", "const char*", "ccp_plus_one", "(const char*)", ' "xY" => "Y"', i=(bytearray(b'xY'),), r='Y') # bytearray object -t(l, "p)p", "const char*", "ccp_plus_one", "(const char*)", ' "xY" => p+1 (~ odd addr)', i=(bytearray(b'xY'),), r=lambda i: type(i) is int and (i%2)==1) # bytearray object -t(l, "p)p", "const char*", "ccp_plus_one", "(const char*)", ' 0xdeadc0de => 0xdeadc0de+1=3735929055', i=(long_h,), r=3735929055) # handle (integer interpreted as ptr) -t(l, "p)p", "const char*", "ccp_plus_one", "(const char*)", ' 0xdeadc0de => 0xdeadc0de+1=3735929055', i=(long_h,), r=3735929055) # handle (integer interpreted as ptr, long in Python 2) -t(l, "p)p", "const char*", "ccp_plus_one", "(const char*)", ' NULL => NULL+1=1', i=( None,), r=1) # NULL, adding one will result in 0x1 - -# functions that change buffers -theader('TESTS OF IMMUTABLE AND MUTABLE PYTHON BUFFERS:') -t(l, "Z)v", "const char*", "cp_head_incr", "(const char*)", ' "string" => None / arg => "string" (not modified)"', i=( 'string',)) # string object -t(l, "Z)v", "const char*", "cp_head_incr", "(const char*)", ' "UnIcOdE" => None / arg => "UnIcOdE" (not modified)"', i=( u'UnIcOdE',)) # string object (unicode in Python 2) -t(l, "Z)v", "const char*", "cp_head_incr", "(const char*)", ' "BCssk#" => None / arg => "BCssk#" (not modified)"', i=( b'BCssk#',)) # bytes object -t(l, "Z)v", "const char*", "cp_head_incr", "(const char*)", ' "xY" => None / arg => "xY" (not modified)"', i=(bytearray(b'xY'),)) # bytearray object -t(l, "p)v", "const char*", "cp_head_incr", "(const char*)", ' "xY" => None / arg => "yY" (!MODIFIED!)"', i=(bytearray(b'xY'),), p=(bytearray(b'yY'),)) # bytearray object - -# tested checked value conversions -theader('ARG & RET TYPE CONVERSION TESTS FOR RANGE CHECKED TYPES:') -t(l, "c)c", "char", "c_plus_one", "(char)", ' "~" => 127', i=( '~',), r=127) -t(l, "c)c", "char", "c_plus_one", "(char)", ' "~" => 127', i=( '~',), r=127) -t(l, "c)c", "char", "c_plus_one", "(char)", ' "" => input exc:', i=( '',), r=Exception) -t(l, "c)c", "char", "c_plus_one", "(char)", ' "" => input exc:', i=( '',), r=Exception) -t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' "ab" => input exc:', i=('ab',), r=Exception) -t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' "ab" => input exc:', i=('ab',), r=Exception) - -t(l, "c)c", "char", "c_plus_one", "(char)", ' -128 => -127', i=(-128,), r=-127) -t(l, "c)c", "char", "c_plus_one", "(char)", ' 127 => -128 (wrapped)', i=( 127,), r=-128) -t(l, "c)c", "char", "c_plus_one", "(char)", ' -129 => input exc:', i=(-129,), r=Exception) -t(l, "c)c", "char", "c_plus_one", "(char)", ' 128 => input exc:', i=( 128,), r=Exception) - -t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' 0 => 1', i=( 0,), r=1) -t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' 255 => 0 (wrapped)', i=( 255,), r=0) -t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' -1 => input exc:', i=( -1,), r=Exception) -t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' 256 => input exc:', i=( 256,), r=Exception) - -t(l, "s)s", "short", "s_plus_one", "(short)", ' -32768 => -32767', i=(-32768,), r=-32767) -t(l, "s)s", "short", "s_plus_one", "(short)", ' 32767 => -32768 (wrapped)',i=( 32767,), r=-32768) -t(l, "s)s", "short", "s_plus_one", "(short)", ' -32769 => input exc:', i=(-32769,), r=Exception) -t(l, "s)s", "short", "s_plus_one", "(short)", ' 32768 => input exc:', i=( 32768,), r=Exception) - -t(l, "S)S", "unsigned short", "us_plus_one", "(unsigned short)", ' 0 => 1', i=( 0,), r=1) -t(l, "S)S", "unsigned short", "us_plus_one", "(unsigned short)", ' 65535 => 0 (wrapped)', i=(65535,), r=0) -t(l, "S)S", "unsigned short", "us_plus_one", "(unsigned short)", ' -1 => input exc:', i=( -1,), r=Exception) -t(l, "S)S", "unsigned short", "us_plus_one", "(unsigned short)", ' 65536 => input exc:', i=(65536,), r=Exception) - -t(l, "p)Z", "const char*", "ccp_plus_one", "(const char*)", '"w/pointer" => input exc:',i=( 'w/pointer',), r=Exception) # string object, not passable as 'p'ointer -t(l, "p)Z", "const char*", "ccp_plus_one", "(const char*)", '"X_unicode" => input exc:',i=(u'X_unicode',), r=Exception) # string object (unicode in Python 2), not passable as 'p'ointer -t(l, "p)Z", "const char*", "ccp_plus_one", "(const char*)", '"1less/ptr" => input exc:',i=(b'1less/ptr',), r=Exception) # bytes object, not passable as 'p'ointer -t(l, "p)p", "const char*", "ccp_plus_one", "(const char*)", ' "x" => input exc:',i=( 'x',), r=Exception) # string object, not passable as 'p'ointer - diff -r 2725de59454a -r 8e905c0798c7 python/pydc/test/types_test.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/pydc/test/types_test.py Wed Aug 03 15:38:07 2022 +0200 @@ -0,0 +1,200 @@ +import pydc +import sys +import platform +import copy +import types + + + +def theader(title): + print("\n"+title) + print('%8s %20s %16s %-20s %11s %-16s -> %16s %-16s %12s %-16s # %s' % ('DC_SIG', 'C_RET_T', 'C_FSYM', 'C_PARAMLIST', 'PY_ARG_T', 'IN_ARGS', 'RET_VAL', 'PY_RET_T', 'OUT_ARGS_T', 'OUT_ARGS', 'NOTES')) + + +def t(lib, dcsig, c_rtype, c_fsym, c_paramlist, extra_msg, **kwargs): + args = kwargs['i'] if 'i' in kwargs else () + post_args = kwargs['p'] if 'p' in kwargs else copy.deepcopy(args) # expected args after call (as some can be modified in-place) + exp_ret = kwargs['r'] if 'r' in kwargs else None # expected return value + err_sgr = '' + # before call + inarg_types = '('+','.join(map(lambda x: type(x).__name__, args))+')' + inargs_str = ','.join(map(str, args)) + # call + try: + fp = pydc.find(lib, c_fsym) + r = pydc.call(fp, dcsig, *args) + rt = type(r).__name__ + if(r != exp_ret or args != post_args): # @@@ type test also or type(r) is not type(exp_ret)): + if((type(exp_ret) is not types.LambdaType) or exp_ret(r) == False): + err_sgr = '\033[41m' + except: + r = '[EXCEPTION]' + rt = '!' + if(exp_ret != Exception): + err_sgr = '\033[41m' + e = str(sys.exc_info()[1]) + extra_msg += ' "'+(e if len(e)<32 else e[0:32]+'...')+'"' + # after call + outarg_types = '('+','.join(map(lambda x: type(x).__name__, args))+')' + outargs = ','.join(map(str, args)) + print('%s%8s %20s %16s %-20s %11s \033[33m%-16s\033[39m -> \033[32m%16.16s\033[39m %-16s %12s \033[32m%-16s\033[0m # %s' % (err_sgr, dcsig, c_rtype, c_fsym, c_paramlist, inarg_types, inargs_str, str(r), '('+rt+')', outarg_types, outargs, extra_msg)) + + return r + + + +# some libc tests ------------------ + +try: + if len(sys.argv) > 1: + libc = pydc.load(sys.argv[1]) + elif sys.platform == "win32": + libc = pydc.load("msvcrt") + elif sys.platform == "darwin": + libc = pydc.load("/usr/lib/libc.dylib") + elif "bsd" in sys.platform: + libc = pydc.load("/usr/lib/libc.so") + #libc = pydc.load("/lib/libc.so.7") + elif platform.architecture()[0] == "64bit": + libc = pydc.load("/lib64/libc.so.6") + else: + libc = pydc.load("/lib/libc.so") + + theader('CLIB TESTS:') + + # void() + t(libc, ")v", "void", "sranddev", "(void)", '') + + # int() + t(libc, ")i", "int", "rand", "(void)", '', r=lambda i: type(i) is int) + + # void(unsigned int) + t(libc, "I)v", "void", "srand", "(int)", '', i=(123,)) + + # int() (and one different helper call for test) + x = \ + t(libc, ")i", "int", "rand", "(void)", 'with seed <------,', r=lambda i: type(i) is int) + t(libc, "I)v", "void", "srand", "(int)", 'set same seed |', i=(123,)) + t(libc, ")i", "int", "rand", "(void)", 'should be same result...', r=x) + t(libc, ")i", "int", "rand", "(void)", '...and now different result', r=lambda i: type(i) is int and i!=x) + + # int(int) + t(libc, "i)i", "int", "abs", "(int)", ' 10 => 10', i=( 10,), r=10) + t(libc, "i)i", "int", "abs", "(int)", ' 0 => 0', i=( 0,), r=0) + t(libc, "i)i", "int", "abs", "(int)", ' -9209 => 9209', i=(-9209,), r=9209) + + # long(long) + t(libc, "j)j", "long", "labs", "(long)", ' 48 => 48', i=( 48,), r=48) + t(libc, "j)j", "long", "labs", "(long)", ' 0 => 0', i=( 0,), r=0) + t(libc, "j)j", "long", "labs", "(long)", '-4271477497 => 4271477497', i=(-4271477497,), r=4271477497) + + # long long(long long) + t(libc, "l)l", "long long", "labs", "(long long)", ' 6334810198 => 6334810198', i=( 6334810198,), r=6334810198) + t(libc, "l)l", "long long", "labs", "(long long)", ' 1 => 1', i=( 1,), r=1) + t(libc, "l)l", "long long", "labs", "(long long)", ' 0 => 0', i=( 0,), r=0) + t(libc, "l)l", "long long", "labs", "(long long)", ' -1 => 1', i=( -1,), r=1) + t(libc, "l)l", "long long", "labs", "(long long)", '-7358758407 => 7358758407', i=(-7358758407,), r=7358758407) + + pydc.free(libc) +except: + print("\033[33mskipping clib tests because: "+str(sys.exc_info()[1])+"\033[0m\nnote: c-lib to use can be specified as command line param") + + +# tests with own .so for testing all conversions ------------------ + +l = pydc.load(sys.path[0]+"/test.so") + +# "long" typed test value use for Python 2 +long_i = 11234 +long_h = 0xdeadc0de +if sys.version_info < (3, 0): + long_i = long(11234) + long_h = long(0xdeadc0de) + +# test all possible arg types and their conversions to and from python, with +# specific focus/tests in areas where python 2 and 3 differ +theader('ARG & RET TYPE CONVERSION TESTS:') +t(l, "B)B", "int", "i_plus_one", "(int)", ' False => True (using int func in C)', i=(False,), r=True) + +t(l, "c)c", "char", "c_plus_one", "(char)", ' "a" (97) => 98', i=( 'a',), r=98) +t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' "a" (97) => 98', i=( 'a',), r=98) +t(l, "c)c", "char", "c_plus_one", "(char)", ' -2 => -1', i=( -2,), r=-1) +t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' 10 => 11', i=( 10,), r=11) + +t(l, "s)s", "short", "s_plus_one", "(short)", ' 10 => 11', i=( 10,), r=11) +t(l, "S)S", "unsigned short", "us_plus_one", "(unsigned short)", ' 10 => 11', i=( 10,), r=11) + +t(l, "i)i", "int", "i_plus_one", "(int)", ' 10 => 11', i=( 10,), r=11) +t(l, "I)I", "unsigned int", "ui_plus_one", "(unsigned int)", ' 10 => 11', i=( 10,), r=11) + +t(l, "j)j", "long", "l_plus_one", "(long)", ' 10 => 11', i=( 10,), r=11) +t(l, "J)J", "unsigned long", "ul_plus_one", "(unsigned long)", ' 10 => 11', i=( 10,), r=11) + +t(l, "l)l", "long long", "ll_plus_one", "(long long)", ' 10 => 11', i=( 10,), r=11) +t(l, "L)L", "unsigned long long", "ull_plus_one", "(unsigned long long)", ' 10 => 11', i=( 10,), r=11) +t(l, "l)l", "long long", "ll_plus_one", "(long long)", ' 11234 => 11235', i=(long_i,), r=11235) +t(l, "L)L", "unsigned long long", "ull_plus_one", "(unsigned long long)", ' 11234 => 11235', i=(long_i,), r=11235) + +t(l, "f)f", "float", "f_plus_one", "(float)", ' -1.23 => -0.23... (w/ fp imprecision)', i=( -1.23,), r=-0.23000001907348633) +t(l, "d)d", "double", "d_plus_one", "(double)", ' 5.67 => 6.67', i=( 5.67,), r= 6.67) + +t(l, "Z)Z", "const char*", "ccp_plus_one", "(const char*)", '"lose char" => "ose char"', i=( 'lose char',), r= 'ose char') # string object +t(l, "Z)Z", "const char*", "ccp_plus_one", "(const char*)", '"X_unicode" => "_unicode"', i=( u'X_unicode',), r=u'_unicode') # string object (unicode in Python 2) +t(l, "Z)Z", "const char*", "ccp_plus_one", "(const char*)", '"1lessbyte" => "lessbyte"', i=( b'1lessbyte',), r= 'lessbyte') # bytes object +t(l, "Z)Z", "const char*", "ccp_plus_one", "(const char*)", ' "xY" => "Y"', i=(bytearray(b'xY'),), r= 'Y') # bytearray object + +t(l, "p)Z", "const char*", "ccp_plus_one", "(const char*)", ' "xY" => "Y"', i=(bytearray(b'xY'),), r='Y') # bytearray object +t(l, "p)p", "const char*", "ccp_plus_one", "(const char*)", ' "xY" => p+1 (~ odd addr)', i=(bytearray(b'xY'),), r=lambda i: type(i) is int and (i%2)==1) # bytearray object +t(l, "p)p", "const char*", "ccp_plus_one", "(const char*)", ' 0xdeadc0de => 0xdeadc0de+1=3735929055', i=(long_h,), r=3735929055) # handle (integer interpreted as ptr) +t(l, "p)p", "const char*", "ccp_plus_one", "(const char*)", ' 0xdeadc0de => 0xdeadc0de+1=3735929055', i=(long_h,), r=3735929055) # handle (integer interpreted as ptr, long in Python 2) +t(l, "p)p", "const char*", "ccp_plus_one", "(const char*)", ' NULL => NULL+1=1', i=( None,), r=1) # NULL, adding one will result in 0x1 + +# helper func to test p2Z +t(l, "p)p", "const char*", "ccp_plus_one", "(const char*)", ' "xY" => "Y" (after p2Z())', i=(bytearray(b'xY'),), r=lambda i: type(i) is int and pydc.p2Z(i) == 'Y') +t(l, "Z)p", "const char*", "ccp_plus_one", "(const char*)", ' "Y" => "" (after p2Z())', i=( 'Y',), r=lambda i: type(i) is int and pydc.p2Z(i) == '') +t(l, "Z)p", "const char*", "ccp_plus_one", "(const char*)", '"X_unicode" => "_unicode" (after p2Z())', i=( u'X_unicode',), r=lambda i: type(i) is int and pydc.p2Z(i) == '_unicode') +t(l, "Z)p", "const char*", "ccp_plus_one", "(const char*)", '"1lessbyte" => "lessbyte" (after p2Z())', i=( b'1lessbyte',), r=lambda i: type(i) is int and pydc.p2Z(i) == 'lessbyte') +t(l, "j)p", "long", "l_plus_one", "(long)", ' -0x1 => "" (after p2Z())', i=( -0x1,), r=lambda i: type(i) is int and pydc.p2Z(i) == None) + +# functions that change buffers +theader('TESTS OF IMMUTABLE AND MUTABLE PYTHON BUFFERS:') +t(l, "Z)v", "const char*", "cp_head_incr", "(const char*)", ' "string" => None / arg => "string" (not modified)"', i=( 'string',)) # string object +t(l, "Z)v", "const char*", "cp_head_incr", "(const char*)", ' "UnIcOdE" => None / arg => "UnIcOdE" (not modified)"', i=( u'UnIcOdE',)) # string object (unicode in Python 2) +t(l, "Z)v", "const char*", "cp_head_incr", "(const char*)", ' "BCssk#" => None / arg => "BCssk#" (not modified)"', i=( b'BCssk#',)) # bytes object +t(l, "Z)v", "const char*", "cp_head_incr", "(const char*)", ' "xY" => None / arg => "xY" (not modified)"', i=(bytearray(b'xY'),)) # bytearray object +t(l, "p)v", "const char*", "cp_head_incr", "(const char*)", ' "xY" => None / arg => "yY" (!MODIFIED!)"', i=(bytearray(b'xY'),), p=(bytearray(b'yY'),)) # bytearray object + +# tested checked value conversions +theader('ARG & RET TYPE CONVERSION TESTS FOR RANGE CHECKED TYPES:') +t(l, "c)c", "char", "c_plus_one", "(char)", ' "~" => 127', i=( '~',), r=127) +t(l, "c)c", "char", "c_plus_one", "(char)", ' "~" => 127', i=( '~',), r=127) +t(l, "c)c", "char", "c_plus_one", "(char)", ' "" => input exc:', i=( '',), r=Exception) +t(l, "c)c", "char", "c_plus_one", "(char)", ' "" => input exc:', i=( '',), r=Exception) +t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' "ab" => input exc:', i=('ab',), r=Exception) +t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' "ab" => input exc:', i=('ab',), r=Exception) + +t(l, "c)c", "char", "c_plus_one", "(char)", ' -128 => -127', i=(-128,), r=-127) +t(l, "c)c", "char", "c_plus_one", "(char)", ' 127 => -128 (wrapped)', i=( 127,), r=-128) +t(l, "c)c", "char", "c_plus_one", "(char)", ' -129 => input exc:', i=(-129,), r=Exception) +t(l, "c)c", "char", "c_plus_one", "(char)", ' 128 => input exc:', i=( 128,), r=Exception) + +t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' 0 => 1', i=( 0,), r=1) +t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' 255 => 0 (wrapped)', i=( 255,), r=0) +t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' -1 => input exc:', i=( -1,), r=Exception) +t(l, "C)C", "unsigned char", "uc_plus_one", "(unsigned char)", ' 256 => input exc:', i=( 256,), r=Exception) + +t(l, "s)s", "short", "s_plus_one", "(short)", ' -32768 => -32767', i=(-32768,), r=-32767) +t(l, "s)s", "short", "s_plus_one", "(short)", ' 32767 => -32768 (wrapped)',i=( 32767,), r=-32768) +t(l, "s)s", "short", "s_plus_one", "(short)", ' -32769 => input exc:', i=(-32769,), r=Exception) +t(l, "s)s", "short", "s_plus_one", "(short)", ' 32768 => input exc:', i=( 32768,), r=Exception) + +t(l, "S)S", "unsigned short", "us_plus_one", "(unsigned short)", ' 0 => 1', i=( 0,), r=1) +t(l, "S)S", "unsigned short", "us_plus_one", "(unsigned short)", ' 65535 => 0 (wrapped)', i=(65535,), r=0) +t(l, "S)S", "unsigned short", "us_plus_one", "(unsigned short)", ' -1 => input exc:', i=( -1,), r=Exception) +t(l, "S)S", "unsigned short", "us_plus_one", "(unsigned short)", ' 65536 => input exc:', i=(65536,), r=Exception) + +t(l, "p)Z", "const char*", "ccp_plus_one", "(const char*)", '"w/pointer" => input exc:',i=( 'w/pointer',), r=Exception) # string object, not passable as 'p'ointer +t(l, "p)Z", "const char*", "ccp_plus_one", "(const char*)", '"X_unicode" => input exc:',i=(u'X_unicode',), r=Exception) # string object (unicode in Python 2), not passable as 'p'ointer +t(l, "p)Z", "const char*", "ccp_plus_one", "(const char*)", '"1less/ptr" => input exc:',i=(b'1less/ptr',), r=Exception) # bytes object, not passable as 'p'ointer +t(l, "p)p", "const char*", "ccp_plus_one", "(const char*)", ' "x" => input exc:',i=( 'x',), r=Exception) # string object, not passable as 'p'ointer +