changeset 34:2682a627168c

- breaking changes: * restrict 'Z' conversions to immutable types * restrict 'p' to mutable types (and handles)
author Tassilo Philipp
date Sun, 12 Apr 2020 19:37:37 +0200
parents ba47a3d709d7
children 75fe1dec0eb4
files python/pydc/README.txt python/pydc/pydcext.c python/pydc/setup.py python/pydc/test/Makefile python/pydc/test/types.py
diffstat 5 files changed, 107 insertions(+), 121 deletions(-) [+]
line wrap: on
line diff
--- a/python/pydc/README.txt	Sat Apr 11 20:00:25 2020 +0200
+++ b/python/pydc/README.txt	Sun Apr 12 19:37:37 2020 +0200
@@ -7,6 +7,9 @@
 Apr 19, 2018: update to dyncall 1.0
 Apr  7, 2020: update to dyncall 1.1, Python 3 support, using the Capsule
               API, as well as support for python unicode strings
+Apr 11, 2020: support for getting loaded library path
+Apr 12, 2020: breaking change: restrict 'Z' conversions to immutable types
+              and 'p' to mutable types (and handles)
 
 
 BUILD/INSTALLATION
@@ -40,30 +43,41 @@
 
     x is positional parameter-type charcode, y is result-type charcode
 
-  SIG | FROM PYTHON 2                                        | FROM PYTHON 3                            | C/C++                           | TO PYTHON 2                          | TO PYTHON 3
-  ----+------------------------------------------------------+------------------------------------------+---------------------------------+--------------------------------------+-------------------------------------
-  'v' |                                                      |                                          | void                            | None (Py_None) (e.g. ret of "...)v") | None (Py_None) (e.g. ret of "...)v")
-  'B' | bool (PyBool)                                        | bool (PyBool)#                           | int/bool                        | bool (PyBool)                        | bool (PyBool)@
-  'c' | int (PyLong)%, str (single char)%                    | int (PyLong)%, str (single char)%        | char                            | int (PyInt)                          | int (PyLong)
-  'C' | int (PyLong)%, str (single char)%                    | int (PyLong)%, str (single char)%        | unsigned char                   | int (PyInt)                          | int (PyLong)
-  's' | int (PyInt)%                                         | int (PyLong)%                            | short                           | int (PyInt)                          | int (PyLong)
-  'S' | int (PyInt)%                                         | int (PyLong)%                            | unsigned short                  | int (PyInt)                          | int (PyLong)
-  'i' | int (PyInt)                                          | int (PyLong)                             | int                             | int (PyInt)                          | int (PyLong)
-  'I' | int (PyInt)                                          | int (PyLong)                             | unsigned int                    | int (PyInt)                          | int (PyLong)
-  'j' | int (PyInt)                                          | int (PyLong)                             | long                            | int (PyInt)                          | int (PyLong)
-  'J' | int (PyInt)                                          | int (PyLong)                             | unsigned long                   | int (PyInt)                          | int (PyLong)
-  'l' | int (PyInt), long (PyLong)                           | int (PyLongLong)                         | long long                       | long (PyLong)                        | int (PyLong)
-  'L' | int (PyInt), long (PyLong)                           | int (PyLongLong)                         | unsigned long long              | long (PyLong)                        | int (PyLong)
-  'f' | float (PyFloat)$                                     | float (PyFloat)$                         | float                           | float (PyFloat)^                     | float (PyFloat)^
-  'd' | float (PyFloat)                                      | float (PyFloat)                          | double                          | float (PyFloat)                      | float (PyFloat)
-  'p' | str (PyUnicode/PyString), int (PyInt), long (PyLong) | str (PyUnicode), int (PyLong), (PyBytes) | void*                           | int,long (Py_ssize_t)                | int (Py_ssize_t)
-  'Z' | str (PyUnicode/PyString)                             | str (PyUnicode), (PyBytes)               | const char* (UTF-8 for unicode) | int (PyString)                       | str (PyUnicode)
+  SIG | FROM PYTHON 2                   | FROM PYTHON 3                   | C/C++                           | TO PYTHON 2                          | TO PYTHON 3
+  ----+---------------------------------+---------------------------------+---------------------------------+--------------------------------------+---------------------------------------
+  'v' |                                 |                                 | void                            | None (Py_None) (e.g. ret of "...)v") | None (Py_None) (e.g. ret of "...)v")
+  'B' | bool (PyBool)                   | bool (PyBool)                 # | int/bool                        | bool (PyBool)                        | bool (PyBool)                        @
+  'c' | int (PyInt)                   % | int (PyLong)                  % | char                            | int (PyInt)                          | int (PyLong)
+      | str (with only a single char) % | str (with only a single char) % | char                            | int (PyInt)                          | int (PyLong)
+  'C' | int (PyInt)                   % | int (PyLong)                  % | unsigned char                   | int (PyInt)                          | int (PyLong)
+      | str (with only a single char) % | str (with only a single char) % | unsigned char                   | int (PyInt)                          | int (PyLong)
+  's' | int (PyInt)                   % | int (PyLong)                  % | short                           | int (PyInt)                          | int (PyLong)
+  'S' | int (PyInt)                   % | int (PyLong)                  % | unsigned short                  | int (PyInt)                          | int (PyLong)
+  'i' | int (PyInt)                     | int (PyLong)                    | int                             | int (PyInt)                          | int (PyLong)
+  'I' | int (PyInt)                     | int (PyLong)                    | unsigned int                    | int (PyInt)                          | int (PyLong)
+  'j' | int (PyInt)                     | int (PyLong)                    | long                            | int (PyInt)                          | int (PyLong)
+  'J' | int (PyInt)                     | int (PyLong)                    | unsigned long                   | int (PyInt)                          | int (PyLong)
+  'l' | int (PyInt)                     | int (PyLongLong)                | long long                       | long (PyLong)                        | int (PyLong)
+      | long (PyLong)                   | -                               | long long                       | long (PyLong)                        | int (PyLong)
+  'L' | int (PyInt)                     | int (PyLongLong)                | unsigned long long              | long (PyLong)                        | int (PyLong)
+      | long (PyLong)                   | -                               | unsigned long long              | long (PyLong)                        | int (PyLong)
+  'f' | float (PyFloat)               $ | float (PyFloat)               $ | float                           | float (PyFloat)                    ^ | float (PyFloat)                      ^
+  'd' | float (PyFloat)                 | float (PyFloat)                 | double                          | float (PyFloat)                      | float (PyFloat)
+  'p' | bytearray (PyByteArray)       & | bytearray (PyByteArray)       & | void*                           | int,long (Py_ssize_t)                | int (Py_ssize_t)
+      | int (PyInt)                     | int (PyLong)                    | void*                           | int,long (Py_ssize_t)                | int (Py_ssize_t)
+      | long (PyLong)                   | -                               | void*                           | int,long (Py_ssize_t)                | int (Py_ssize_t)
+  'Z' | str (PyString)                ! | str (PyUnicode)               ! | const char* (UTF-8 for unicode) | int (PyString)                       | str (PyUnicode)
+      | unicode (PyUnicode)           ! | -                               | const char* (UTF-8 for unicode) | int (PyString)                       | str (PyUnicode)
+      | -                               | bytes (PyBytes)               ! | const char* (UTF-8 for unicode) | int (PyString)                       | str (PyUnicode)
+      | bytearray (PyByteArray)       ! | bytearray (PyByteArray)       ! | const char* (UTF-8 for unicode) | int (PyString)                       | str (PyUnicode)
 
 # converted to 1 if True and 0 otherwise
 @ converted to False if 0 and True otherwise
-% range checked
+% range/length checked
 $ cast to single precision
 ^ cast to double precision
+& mutable buffer when passed to C
+! immutable buffer when passed to C, as strings (in any form) are considered objects, not buffers
 
 TODO
 ====
--- a/python/pydc/pydcext.c	Sat Apr 11 20:00:25 2020 +0200
+++ b/python/pydc/pydcext.c	Sun Apr 12 19:37:37 2020 +0200
@@ -325,15 +325,11 @@
 				dcArgDouble(gpCall, PyFloat_AsDouble(po));
 				break;
 
-			case DC_SIGCHAR_POINTER:
+			case DC_SIGCHAR_POINTER: // this will only accept integers or mutable array types (meaning only bytearray)
 			{
-				PyObject* bo = NULL;
 				DCpointer p;
-				if ( PyUnicode_Check(po) ) {
-					if((bo = PyUnicode_AsEncodedString(po, "utf-8", "strict")))  // !new ref!
-						p = PyBytes_AS_STRING(bo); // Borrowed pointer
-				} else if ( DcPyString_Check(po) )
-					p = (DCpointer) DcPyString_AsString(po);
+				if ( PyByteArray_Check(po) )
+					p = (DCpointer) PyByteArray_AsString(po); // adds an extra '\0', but that's ok
 #if PY_MAJOR_VERSION < 3
 				else if ( PyInt_Check(po) )
 					p = (DCpointer) PyInt_AS_LONG(po);
@@ -341,25 +337,34 @@
 				else if ( PyLong_Check(po) )
 					p = (DCpointer) PyLong_AsVoidPtr(po);
 				else
-					return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a promoting pointer-type (int,str)", pos );
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a promoting pointer-type (int, bytearray)", pos );
 				dcArgPointer(gpCall, p);
-				Py_XDECREF(bo);
 			}
 			break;
 
-			case DC_SIGCHAR_STRING:
+			case DC_SIGCHAR_STRING: // strings are considered to be immutable objects
 			{
 				PyObject* bo = NULL;
 				const char* p;
+				char* p_;
+				size_t s;
 				if ( PyUnicode_Check(po) ) {
 					if((bo = PyUnicode_AsEncodedString(po, "utf-8", "strict")))  // !new ref!
 						p = PyBytes_AS_STRING(bo); // Borrowed pointer
-				} else if ( DcPyString_Check(po) ) {
-					p = DcPyString_AsString(po);
-				} else
+				} else if ( DcPyString_Check(po) )
+					p = DcPyString_AsString(po); //@@@ must not be modified in any way
+				else if ( PyByteArray_Check(po) )
+					p = (DCpointer) PyByteArray_AsString(po); // adds an extra '\0', but that's ok //@@@ not sure if allowed to modify
+				else
 					return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a str", pos );
-				dcArgPointer(gpCall, (DCpointer) p);
+
+				// pointer points in any case to a buffer that shouldn't be modified, so pass a copy of the string to dyncall
+				s = strlen(p)+1;
+				p_ = malloc(s);
+				strncpy(p_, p, s);
 				Py_XDECREF(bo);
+				dcArgPointer(gpCall, (DCpointer)p_);
+				free(p_);
 			}
 			break;
 
--- a/python/pydc/setup.py	Sat Apr 11 20:00:25 2020 +0200
+++ b/python/pydc/setup.py	Sun Apr 12 19:37:37 2020 +0200
@@ -7,7 +7,7 @@
 
 setup(
   name             = 'pydc'
-, version          = '1.1'
+, version          = '1.1.1'
 , author           = 'Daniel Adler'
 , author_email     = 'dadler@dyncall.org'
 , maintainer       = 'Daniel Adler'
--- a/python/pydc/test/Makefile	Sat Apr 11 20:00:25 2020 +0200
+++ b/python/pydc/test/Makefile	Sun Apr 12 19:37:37 2020 +0200
@@ -1,37 +1,9 @@
-#from dynload test
-## path to default libc.so file, easier to do via shell than in code (see main() in dynload_plain.c)
-## for compat: first gmake style, then assignment op which will use ! as part of var name on gmake<4
-##             and thus not override previously set var
-#DEF_C_DYLIB=$(shell ((ldd `which ls` | grep -o '/.*/libc.so[^ ]*' || ls /lib*/libc.so* || ls /usr/lib/libc.so*) | grep -v '\.a$$' | (sort -V -r || sort -t . -n -k 2)) 2>/dev/null | head -1)
-#DEF_C_DYLIB!=((ldd `which ls` | grep -o '/.*/libc.so[^ ]*' || ls /lib*/libc.so* || ls /usr/lib/libc.so*) | grep -v '\.a$$' | (sort -V -r || sort -t . -n -k 2)) 2>/dev/null | head -1
-#
-#APP        = dynload_plain
-#OBJS       = dynload_plain.o
-#TEST_U8_SO = dynload_plain_ß_test # @@@ unsure if every platform handles ß, here (ANSI, UTF-8, ...)
-#SRCTOP     = ${VPATH}/../..
-#BLDTOP     = ../..
-#CFLAGS    += -I${SRCTOP}/dynload -DDEF_C_DYLIB=\"${DEF_C_DYLIB}\"
-#LDLIBS_D  += -L${BLDTOP}/dynload -ldynload_s
-#
-## Works on: Darwin, NetBSD.
-# Linux: add '-ldl'
-#.PHONY: all clean install
-#all: ${APP} ${TEST_U8_SO}
-#${APP}: ${OBJS}
-#	${CC} ${LDFLAGS} ${OBJS} ${LDLIBS_D} ${LDLIBS} -o ${APP}
-#${TEST_U8_SO}:
-#	echo 'int dynload_plain_testfunc() { return 5; }' | ${CC} -`[ \`uname\` = Darwin ] && echo dynamiclib || echo shared` -x c - -o ${TEST_U8_SO}
-#clean:
-#	rm -f ${APP} ${OBJS} ${TEST_U8_SO}
-#install:
-#	mkdir -p ${PREFIX}/test
-#	cp ${APP} ${TEST_U8_SO} ${PREFIX}/test
-
 .PHONY: test.so
 
 test.so:
-	for i in char 'unsigned char' short 'unsigned short' int 'unsigned int' long 'unsigned long' 'long long' 'unsigned long long' float double 'const char*'; do \
-		echo "$$i `echo $$i | sed -E 's/(^| )([a-z])[^ ]*/\2/g'`_plus_one($$i v) { return v+1; }"; \
-	done | ${CC} -`[ \`uname\` = Darwin ] && echo dynamiclib || echo shared` -x c - -o $@
-	echo Done! Now you can run types.py
+	(for i in char 'unsigned char' short 'unsigned short' int 'unsigned int' long 'unsigned long' 'long long' 'unsigned long long' float double 'const char*'; do \
+		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
 
--- a/python/pydc/test/types.py	Sat Apr 11 20:00:25 2020 +0200
+++ b/python/pydc/test/types.py	Sun Apr 12 19:37:37 2020 +0200
@@ -6,11 +6,15 @@
 
 
 def theader(title):
-  print(title)
-  print('%8s %20s %16s %-20s %12s %-12s -> %16s %-16s    # %s' % ('DC_SIG', 'C_RET_T', 'C_FSYM', 'C_PARAMLIST', 'PYTHON_ARG_T', 'IN_ARGS', 'RET_VAL', 'PYTHON_RET_T', 'NOTES'))
+  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, *args):
+  # before call
+  inarg_types = '('+','.join(map(lambda x: type(x).__name__, args))+')'
+  inargs = ','.join(map(str, args))
+  # call
   try:
     fp = pydc.find(lib, c_fsym)
     r = pydc.call(fp, dcsig, *args)
@@ -18,11 +22,12 @@
   except:
     r = '[EXCEPTION]'
     rt = '!'
-    extra_msg += ' "'+str(sys.exc_info()[1])+'"'
-
-  inarg_types = '('+','.join(map(lambda x: type(x).__name__, args))+')'
-  inargs = ','.join(map(str, args))
-  print('%8s %20s %16s %-20s %12s %-12s -> %16.16s %-16s    # %s' % (dcsig, c_rtype, c_fsym, c_paramlist, inarg_types, inargs, str(r), '('+rt+')', extra_msg))
+    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('%8s %20s %16s %-20s %11s \033[33m%-16s\033[0m -> \033[32m%16.16s\033[0m %-16s %12s \033[32m%-16s\033[0m  # %s' % (dcsig, c_rtype, c_fsym, c_paramlist, inarg_types, inargs, str(r), '('+rt+')', outarg_types, outargs, extra_msg))
 
 
 
@@ -84,28 +89,57 @@
 
 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)', False)
+
 t(l, "c)c", "char",                 "c_plus_one", "(char)",               '   "a" (97) =>    98',                         'a')
 t(l, "C)C", "unsigned char",       "uc_plus_one", "(unsigned char)",      '   "a" (97) =>    98',                         'a')
 t(l, "c)c", "char",                 "c_plus_one", "(char)",               '         -2 =>    -1',                          -2)
 t(l, "C)C", "unsigned char",       "uc_plus_one", "(unsigned char)",      '         10 =>    11',                          10)
+
 t(l, "s)s", "short",                "s_plus_one", "(short)",              '         10 =>    11',                          10)
 t(l, "S)S", "unsigned short",      "us_plus_one", "(unsigned short)",     '         10 =>    11',                          10)
+
 t(l, "i)i", "int",                  "i_plus_one", "(int)",                '         10 =>    11',                          10)
 t(l, "I)I", "unsigned int",        "ui_plus_one", "(unsigned int)",       '         10 =>    11',                          10)
+
 t(l, "j)j", "long",                 "l_plus_one", "(long)",               '         10 =>    11',                          10)
 t(l, "J)J", "unsigned long",       "ul_plus_one", "(unsigned long)",      '         10 =>    11',                          10)
+
 t(l, "l)l", "long long",           "ll_plus_one", "(long long)",          '         10 =>    11',                          10)
 t(l, "L)L", "unsigned long long", "ull_plus_one", "(unsigned long long)", '         10 =>    11',                          10)
+t(l, "l)l", "long long",           "ll_plus_one", "(long long)",          '      11234 => 11235',                      long_i)
+t(l, "L)L", "unsigned long long", "ull_plus_one", "(unsigned long long)", '      11234 => 11235',                      long_i)
+
 t(l, "f)f", "float",                "f_plus_one", "(float)",              '      -1.23 => -0.23',                       -1.23)
 t(l, "d)d", "double",               "d_plus_one", "(double)",             '       5.67 =>  6.67',                        5.67)
-t(l, "Z)Z", "const char*",         "cc_plus_one", "(const char*)",        '"lose char" => "ose char"',            'lose char')
-t(l, "p)Z", "const char*",         "cc_plus_one", "(const char*)",        '"w/pointer" => "/pointer"',            'w/pointer')
-t(l, "p)p", "const char*",         "cc_plus_one", "(const char*)",        '        "x" => p+1 (retval is prob odd addr)', 'x')
-t(l, "p)p", "const char*",         "cc_plus_one", "(const char*)",        ' 0xdeadc0de => 0xdeadc0de+1=3735929055',0xdeadc0de)
+
+t(l, "Z)Z", "const char*",        "ccp_plus_one", "(const char*)",        '"lose char" => "ose char"',            'lose char') # string object
+t(l, "Z)Z", "const char*",        "ccp_plus_one", "(const char*)",        '"X_unicode" => "_unicode"',           u'X_unicode') # string object (unicode in Python 2)
+t(l, "Z)Z", "const char*",        "ccp_plus_one", "(const char*)",        '"1lessbyte" => "lessbyte"',           b'1lessbyte') # bytes object
+t(l, "Z)Z", "const char*",        "ccp_plus_one", "(const char*)",        '       "xY" =>    "Y"',           bytearray(b'xY')) # bytearray object
+
+t(l, "p)Z", "const char*",        "ccp_plus_one", "(const char*)",        '       "xY" =>    "Y"',           bytearray(b'xY')) # bytearray object
+t(l, "p)p", "const char*",        "ccp_plus_one", "(const char*)",        '       "xY" => p+1 (~ odd addr)', bytearray(b'xY')) # bytearray object
+t(l, "p)p", "const char*",        "ccp_plus_one", "(const char*)",        ' 0xdeadc0de => 0xdeadc0de+1=3735929055',    long_h) # handle (integer interpreted as ptr)
+t(l, "p)p", "const char*",        "ccp_plus_one", "(const char*)",        ' 0xdeadc0de => 0xdeadc0de+1=3735929055',    long_h) # handle (integer interpreted as ptr, long in Python 2)
+
+# 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)"',         'string') # string object
+t(l, "Z)v", "const char*",        "cp_head_incr", "(const char*)",        '  "UnIcOdE" => None / arg => "UnIcOdE" (not modified)"',       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)"',        b'BCssk#') # bytes object
+t(l, "Z)v", "const char*",        "cp_head_incr", "(const char*)",        '       "xY" => None / arg => "xY"      (not modified)"', bytearray(b'xY')) # bytearray object
+t(l, "p)v", "const char*",        "cp_head_incr", "(const char*)",        '       "xY" => None / arg => "yY"       (!MODIFIED!)"',  bytearray(b'xY')) # bytearray object
 
 # tested checked value conversions
 theader('ARG & RET TYPE CONVERSION TESTS FOR RANGE CHECKED TYPES:')
@@ -136,47 +170,8 @@
 t(l, "S)S", "unsigned short",      "us_plus_one", "(unsigned short)",     '         -1 => input exc:',          -1)
 t(l, "S)S", "unsigned short",      "us_plus_one", "(unsigned short)",     '      65536 => input exc:',       65536)
 
+t(l, "p)Z", "const char*",        "ccp_plus_one", "(const char*)",        '"w/pointer" => input exc:', 'w/pointer') # string object, not passable as 'p'ointer
+t(l, "p)Z", "const char*",        "ccp_plus_one", "(const char*)",        '"X_unicode" => input exc:',u'X_unicode') # 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:',b'1less/ptr') # bytes object, not passable as 'p'ointer
+t(l, "p)p", "const char*",        "ccp_plus_one", "(const char*)",        '        "x" => input exc:',         'x') # string object, not passable as 'p'ointer
 
-#     int
-#     rand(void);
-#rand()
-#define DC_SIGCHAR_VOID         'v' ra
-#define DC_SIGCHAR_BOOL         'B'
-#define DC_SIGCHAR_CHAR         'c'    2 versions
-#define DC_SIGCHAR_UCHAR        'C'
-#define DC_SIGCHAR_SHORT        's'
-#define DC_SIGCHAR_USHORT       'S'
-#define DC_SIGCHAR_INT          'i' r
-#define DC_SIGCHAR_UINT         'I'  a
-#define DC_SIGCHAR_LONG         'j' ra
-#define DC_SIGCHAR_ULONG        'J'
-#define DC_SIGCHAR_LONGLONG     'l' ra
-#define DC_SIGCHAR_ULONGLONG    'L'
-#define DC_SIGCHAR_FLOAT        'f'
-#define DC_SIGCHAR_DOUBLE       'd'
-#define DC_SIGCHAR_POINTER      'p'
-#define DC_SIGCHAR_STRING       'Z'
-#define DC_SIGCHAR_STRUCT       'T'
-#define DC_SIGCHAR_ENDARG       ')' /* also works for end struct */
-
-
-
-#  SIG | FROM PYTHON 2                      | FROM PYTHON 3 @@@                  | C/C++                           | TO PYTHON 2                        | TO PYTHON 3 @@@
-#  ----+------------------------------------+------------------------------------+---------------------------------+------------------------------------+-----------------------------------
-#  'v' |                                    |                                    | void                            | ?NoneType (returned for "xxx)v")    | NoneType (returned for "xxx)v")
-#  'B' | ?PyBool                             | ?PyBool                             | bool                            | ?PyBool                             | ?PyBool
-#  'c' | ?PyInt (range checked)              | ?PyLong (range checked)             | char                            | ?PyInt                              | ?PyLong
-#  'C' | ?PyInt (range checked)              | ?PyLong (range checked)             | unsigned char                   | ?PyInt                              | ?PyLong
-#  's' | ?PyInt (range checked)              | ?PyLong (range checked)             | short                           | ?PyInt                              | ?PyLong
-#  'S' | ?PyInt (range checked)              | ?PyLong (range checked)             | unsigned short                  | ?PyInt                              | ?PyLong
-#  'i' | ?PyInt                              | ?PyLong                             | int                             | ?PyInt                              | ?PyLong
-#  'I' | ?PyInt                              | ?PyLong                             | unsigned int                    | ?PyInt                              | ?PyLong
-#  'j' | ?PyLong                             | ?PyLong                             | long                            | ?PyLong                             | ?PyLong
-#  'J' | ?PyLong                             | ?PyLong                             | unsigned long                   | ?PyLong                             | ?PyLong
-#  'l' | ?PyLongLong                         | ?PyLongLong                         | long long                       | ?PyLongLong                         | ?PyLongLong
-#  'L' | ?PyLongLong                         | ?PyLongLong                         | unsigned long long              | ?PyLongLong                         | ?PyLongLong
-#  'f' | ?PyFloat (cast to single precision) | ?PyFloat (cast to single precision) | float                           | ?PyFloat (cast to double precision) | ?PyFloat (cast to double precision)
-#  'd' | ?PyFloat                            | ?PyFloat                            | double                          | ?PyFloat                            | ?PyFloat
-#  'p' | ?PyUnicode/PyString/PyLong          | ?PyUnicode/PyBytes/PyLong           | void*                           | ?Py_ssize_t                         | ?Py_ssize_t
-#  'Z' | ?PyUnicode/PyString                 | ?PyUnicode/PyBytes                  | const char* (UTF-8 for unicode) | ?PyString                           | ?PyUnicode
-