diff python/pydc/pydcext.c @ 29:6cc2b7fc7ea2

bigger pydc update: - cleanups and refactoring - python 2 fixes in var conversions (especially w/ respect to int vs long) - fix to pydc.free() which didn't work at all - fix to return python bool as actual bool - test lib covering all conversions (manual verification, though :-/)
author Tassilo Philipp
date Wed, 08 Apr 2020 22:17:43 +0200
parents edbbd467f50a
children baf087cf5971
line wrap: on
line diff
--- a/python/pydc/pydcext.c	Tue Apr 07 21:16:37 2020 +0200
+++ b/python/pydc/pydcext.c	Wed Apr 08 22:17:43 2020 +0200
@@ -30,10 +30,11 @@
 #  define USE_CAPSULE_API
 #  define DcPyCObject_FromVoidPtr(ptr, dtor)   PyCapsule_New((ptr), NULL, (dtor))
 #  define DcPyCObject_AsVoidPtr(ppobj)         PyCapsule_GetPointer((ppobj), NULL)
-#  define DcPyCObject_SetVoidPtr(ppobj, ptr)   PyCapsule_SetPointer((ppobj), (ptr))  // this might need to call the dtor to behave like PyCObject_SetVoidPtr?
+#  define DcPyCObject_SetVoidPtr(ppobj, ptr)   //@@@ unsure what to do, cannot/shouldn't call this with a null pointer as this wants to call the dtor, so not doing anything: PyCapsule_SetPointer((ppobj), (ptr))  // this might need to call the dtor to behave like PyCObject_SetVoidPtr?
 #endif
 
 #if PY_MAJOR_VERSION >= 3
+#  define EXPECT_LONG_TYPE_STR "an int"
 #  define DcPyString_GET_SIZE PyBytes_GET_SIZE
 #  define DcPyString_Check    PyBytes_Check
 #  define DcPyString_AsString PyBytes_AsString
@@ -41,6 +42,7 @@
 #  define DcPyInt_AsLong      PyLong_AsLong
 #  define DcPyInt_AS_LONG     PyLong_AS_LONG
 #else
+#  define EXPECT_LONG_TYPE_STR "an int or a long"
 #  define DcPyString_GET_SIZE PyString_GET_SIZE
 #  define DcPyString_Check    PyString_Check
 #  define DcPyString_AsString PyString_AsString
@@ -54,13 +56,13 @@
 #if defined(USE_CAPSULE_API)
 void free_library(PyObject* capsule)
 {
-  void* libhandle = PyCapsule_GetPointer(capsule, NULL);
+	void* libhandle = PyCapsule_GetPointer(capsule, NULL);
 #else
 void free_library(void* libhandle)
 {
 #endif
-  if (libhandle != 0)
-    dlFreeLibrary(libhandle);
+	if (libhandle != 0)
+		dlFreeLibrary(libhandle);
 }
 
 
@@ -69,18 +71,18 @@
 static PyObject*
 pydc_load(PyObject* self, PyObject* args)
 {
-  const char* libpath;
-  void* libhandle;
+	const char* libpath;
+	void* libhandle;
 
-  if (!PyArg_ParseTuple(args,"s", &libpath))
-    return PyErr_Format(PyExc_RuntimeError, "libpath argument (string) missing");
+	if (!PyArg_ParseTuple(args,"s", &libpath))
+		return PyErr_Format(PyExc_RuntimeError, "libpath argument (string) missing");
 
-  libhandle = dlLoadLibrary(libpath);
+	libhandle = dlLoadLibrary(libpath);
 
-  if (!libhandle)
-    return PyErr_Format(PyExc_RuntimeError, "dlLoadLibrary('%s') failed", libpath);
+	if (!libhandle)
+		return PyErr_Format(PyExc_RuntimeError, "dlLoadLibrary('%s') failed", libpath);
 
-  return DcPyCObject_FromVoidPtr(libhandle, &free_library);
+	return DcPyCObject_FromVoidPtr(libhandle, &free_library);
 }
 
 /* find function */
@@ -88,23 +90,23 @@
 static PyObject*
 pydc_find(PyObject* self, PyObject* args)
 {
-  PyObject* pcobj;
-  const char* symbol;
-  void* libhandle;
-  void* funcptr;
+	PyObject* pcobj;
+	const char* symbol;
+	void* libhandle;
+	void* funcptr;
 
-  if (!PyArg_ParseTuple(args, "Os", &pcobj, &symbol))
-    return PyErr_Format(PyExc_RuntimeError, "argument mismatch");
+	if (!PyArg_ParseTuple(args, "Os", &pcobj, &symbol))
+		return PyErr_Format(PyExc_RuntimeError, "argument mismatch");
 
-  libhandle = DcPyCObject_AsVoidPtr(pcobj);
-  if (!libhandle)
-    return PyErr_Format(PyExc_RuntimeError, "libhandle is null");
+	libhandle = DcPyCObject_AsVoidPtr(pcobj);
+	if (!libhandle)
+		return PyErr_Format(PyExc_RuntimeError, "libhandle is null");
 
-  funcptr = dlFindSymbol(libhandle, symbol);
-  if (!funcptr)
-    return PyErr_Format(PyExc_RuntimeError, "symbol '%s' not found", symbol);
+	funcptr = dlFindSymbol(libhandle, symbol);
+	if (!funcptr)
+		return PyErr_Format(PyExc_RuntimeError, "symbol '%s' not found", symbol);
 
-  return DcPyCObject_FromVoidPtr(funcptr, NULL);
+	return DcPyCObject_FromVoidPtr(funcptr, NULL);
 }
 
 /* free function */
@@ -112,20 +114,20 @@
 static PyObject*
 pydc_free(PyObject* self, PyObject* args)
 {
-  PyObject* pcobj;
-  void* libhandle;
+	PyObject* pcobj;
+	void* libhandle;
 
-  if (!PyArg_ParseTuple(args, "o", &pcobj))
-    return PyErr_Format(PyExc_RuntimeError, "argument mismatch");
+	if (!PyArg_ParseTuple(args, "O", &pcobj))
+		return PyErr_Format(PyExc_RuntimeError, "argument mismatch");
 
-  libhandle = DcPyCObject_AsVoidPtr(pcobj);
-  if (!libhandle)
-    return PyErr_Format(PyExc_RuntimeError, "libhandle is NULL");
+	libhandle = DcPyCObject_AsVoidPtr(pcobj);
+	if (!libhandle)
+		return PyErr_Format(PyExc_RuntimeError, "libhandle is NULL");
 
-  dlFreeLibrary(libhandle);
-  DcPyCObject_SetVoidPtr(pcobj, NULL);
+	dlFreeLibrary(libhandle);
+	DcPyCObject_SetVoidPtr(pcobj, NULL);
 
-  Py_RETURN_NONE;
+	Py_RETURN_NONE;
 }
 
 
@@ -134,209 +136,238 @@
 
 DCCallVM* gpCall;
 
+
 /* call function */
 
 static PyObject*
 pydc_call(PyObject* self, PyObject* in_args)
 {
-  PyObject*   pcobj_funcptr;
-  const char* signature;
-  PyObject*   args;
-  int         l;
-  const char* ptr;
-  char        ch;
-  int         pos;
-  void*       pfunc;
+	PyObject    *pcobj_funcptr, *args;
+	const char  *signature, *ptr;
+	char        ch;
+	int         pos, ts;
+	void*       pfunc;
+
+	if (!PyArg_ParseTuple(in_args,"OsO", &pcobj_funcptr, &signature, &args))
+		return PyErr_Format(PyExc_RuntimeError, "argument mismatch");
+
+	pfunc = DcPyCObject_AsVoidPtr(pcobj_funcptr);
+	if (!pfunc)
+		return PyErr_Format( PyExc_RuntimeError, "function pointer is NULL" );
 
-  if (!PyArg_ParseTuple(in_args,"OsO", &pcobj_funcptr, &signature, &args))
-    return PyErr_Format(PyExc_RuntimeError, "argument mismatch");
+	ptr = signature;
+	pos = 0;
+	ts  = PyTuple_Size(args);
+
+	dcReset(gpCall);
 
-  pfunc = DcPyCObject_AsVoidPtr(pcobj_funcptr);
-  if (!pfunc)
-    return PyErr_Format( PyExc_RuntimeError, "function pointer is NULL" );
+	for (ch = *ptr; ch != '\0' && ch != ')'; ch = *++ptr)
+	{
+		PyObject* po;
+
+		if (pos > ts)
+			return PyErr_Format( PyExc_RuntimeError, "expecting more arguments" );
 
-  l = PyTuple_Size(args);
+		po = PyTuple_GetItem(args, pos);
+
+		++pos; // incr here, code below uses it as 1-based argument index for error strings
 
-  ptr = signature;
-  pos = 0;
-
-  dcReset(gpCall);
+		switch(ch)
+		{
+			case DC_SIGCHAR_BOOL:
+				if ( !PyBool_Check(po) )
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a bool", pos );
+				dcArgBool(gpCall, (Py_True == po) ? DC_TRUE : DC_FALSE);
+				break;
 
-  while ( (ch = *ptr) != '\0' && ch != ')' )
-  {
-    PyObject* po;
-
-    int index = pos+1;
-
-    if (pos > l) return PyErr_Format( PyExc_RuntimeError, "expecting more arguments" );
-
-    po = PyTuple_GetItem(args,pos);
+			case DC_SIGCHAR_CHAR:
+			case DC_SIGCHAR_UCHAR:
+				{
+					DCchar c;
+					if ( PyUnicode_Check(po) )
+					{
+#if (PY_VERSION_HEX < 0x03030000)
+						Py_UNICODE cu;
+						if (PyUnicode_GET_SIZE(po) != 1)
+#else
+						Py_UCS4 cu;
+						if (PyUnicode_GET_LENGTH(po) != 1)
+#endif
+							return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a string with length of 1 (a char string)", pos );
 
-    switch(ch)
-    {
-      case DC_SIGCHAR_BOOL:
-      {
-        DCbool b;
-        if ( !PyBool_Check(po) ) return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting a bool", index );
-        b = (Py_True == po) ? DC_TRUE : DC_FALSE;
-        dcArgBool(gpCall, b);
-      }
-      break;
-      case DC_SIGCHAR_CHAR:
-      case DC_SIGCHAR_UCHAR:
-      {
-        DCchar c;
-        if ( DcPyString_Check(po) )
-        {
-          // Py_ssize_t l;
-          size_t l;
-          char* s;
-          l = DcPyString_GET_SIZE(po);
-          if (l != 1) return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting a string with length of 1 (a char string)", index );
-          s = DcPyString_AsString(po);
-          c = (DCchar) s[0];
-        }
-        else if ( DcPyInt_Check(po) )
-        {
-          long l;
-          l = DcPyInt_AsLong(po);
-          if ( (l > CHAR_MAX) || (l < CHAR_MIN)) return PyErr_Format( PyExc_RuntimeError, "value out of range at argument %d - expecting a char code", index );
-          c = (DCchar) l;
-        }
-        else return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting a char", index );
-        dcArgChar(gpCall, c);
-      }
-      break;
-      case DC_SIGCHAR_SHORT:
-      case DC_SIGCHAR_USHORT:
-      {
-        DCshort s;
-        long v;
-        if ( !DcPyInt_Check(po) )
-          return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting a short int", index );
-        v = DcPyInt_AS_LONG(po);
-        if ( (v < SHRT_MIN) || (v > SHRT_MAX) )
-          return PyErr_Format( PyExc_RuntimeError, "value out of range at argument %d - expecting a short value", index );
-        s = (DCshort) v;
-        dcArgShort(gpCall, s);
-      }
-      break;
-      case DC_SIGCHAR_INT:
-      case DC_SIGCHAR_UINT:
-      {
-        long v;
-        if ( !DcPyInt_Check(po) ) return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting an int", index );
-        v = DcPyInt_AS_LONG(po);
-        dcArgInt(gpCall, (DCint) v );
-      }
-      break;
-      case DC_SIGCHAR_LONG:
-      case DC_SIGCHAR_ULONG:
-      {
-        long v;
-        if ( !DcPyInt_Check(po) ) return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting an int", index );
-        v = DcPyInt_AsLong(po);
+#if (PY_VERSION_HEX < 0x03030000)
+						cu = PyUnicode_AS_UNICODE(po)[0];
+#else
+						cu = PyUnicode_ReadChar(po, 0);
+#endif
+						// check against UCHAR_MAX in every case b/c Py_UCS4 is unsigned
+						if ( (cu > UCHAR_MAX))
+							return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting a char code", pos ); //@@@ error message needs to specify python types
+						c = (DCchar) cu;
+					}
+					else if ( DcPyString_Check(po) )
+					{
+						size_t l;
+						char* s;
+						l = DcPyString_GET_SIZE(po);
+						if (l != 1)
+							return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a string with length of 1 (a char string)", pos );
+						s = DcPyString_AsString(po);
+						c = (DCchar) s[0];
+					}
+					else if ( DcPyInt_Check(po) )
+					{
+						long l = DcPyInt_AsLong(po);
+						if (ch == DC_SIGCHAR_CHAR && (l < CHAR_MIN || l > CHAR_MAX))
+							return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting %d <= arg <= %d, got %ld", pos, CHAR_MIN, CHAR_MAX, l );
+						if (ch == DC_SIGCHAR_UCHAR && (l < 0 || l > UCHAR_MAX))
+							return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting 0 <= arg <= %d, got %ld", pos, UCHAR_MAX, l );
+						c = (DCchar) l;
+					}
+					else
+						return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a char", pos );
+					dcArgChar(gpCall, c);
+				}
+				break;
+
+			case DC_SIGCHAR_SHORT:
+				{
+					long l;
+					if ( !DcPyInt_Check(po) )
+						return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting an int", pos );
+					l = DcPyInt_AS_LONG(po);
+					if (l < SHRT_MIN || l > SHRT_MAX)
+						return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting %d <= arg <= %d, got %ld", pos, SHRT_MIN, SHRT_MAX, l );
+					dcArgShort(gpCall, (DCshort)l);
+				}
+				break;
+
+			case DC_SIGCHAR_USHORT:
+				{
+					long l;
+					if ( !DcPyInt_Check(po) )
+						return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting an int", pos );
+					l = DcPyInt_AS_LONG(po);
+					if (l < 0 || l > USHRT_MAX)
+						return PyErr_Format( PyExc_RuntimeError, "arg %d out of range - expecting 0 <= arg <= %d, got %ld", pos, USHRT_MAX, l );
+					dcArgShort(gpCall, (DCshort)l);
+				}
+				break;
 
-      }
-      break;
-      case DC_SIGCHAR_LONGLONG:
-      case DC_SIGCHAR_ULONGLONG:
-      {
-        PY_LONG_LONG pl;
-        DClonglong dl;
-        if ( !PyLong_Check(po) ) return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting a long long", index );
-        pl = PyLong_AsLongLong(po);
-        dl = (DClonglong) pl;
-        dcArgLongLong(gpCall, dl );
-      }
-      break;
-      case DC_SIGCHAR_FLOAT:
-      {
-        DCfloat f;
-        if (!PyFloat_Check(po)) return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expeecting a float", index );
-        f = (float) PyFloat_AsDouble(po);
-        dcArgFloat(gpCall, f);
-      }
-      break;
-      case DC_SIGCHAR_DOUBLE:
-      {
-        double d;
-        if (!PyFloat_Check(po)) return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expeecting a float", index );
-        d = PyFloat_AsDouble(po);
-        dcArgDouble(gpCall, d);
-      }
-      break;
-      case DC_SIGCHAR_POINTER:
-      {
-        DCpointer p;
-        if ( PyUnicode_Check(po) ) {
-          PyObject* bo = PyUnicode_AsEncodedString(po, "utf-8", "strict"); // Owned reference @@@
-          if (bo) {
-            p = PyBytes_AS_STRING(bo); // Borrowed pointer
-            //p = strdup(my_result);
-            //Py_DECREF(bo);
-          }
-        } else if ( DcPyString_Check(po) ) {
-          p = (DCpointer) DcPyString_AsString(po);
-        } else if ( PyLong_Check(po) ) {
-          p = (DCpointer) PyLong_AsVoidPtr(po);
-        } else {
-          return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting a promoting pointer-type (int,string)", index );
-        }
-        dcArgPointer(gpCall, p);
-      }
-      break;
-      case DC_SIGCHAR_STRING:
-      {
-        const char* p;
-        if ( PyUnicode_Check(po) ) {
-          PyObject* bo = PyUnicode_AsEncodedString(po, "utf-8", "strict"); // Owned reference @@@
-          if (bo) {
-            p = PyBytes_AS_STRING(bo); // Borrowed pointer
-            //p = strdup(my_result);
-            //Py_DECREF(bo);
-          }
-        } else if ( DcPyString_Check(po) ) {
-          p = DcPyString_AsString(po);
-        } else {
-          return PyErr_Format( PyExc_RuntimeError, "argument mismatch at pos %d - expecting a string", index );
-        }
-        dcArgPointer(gpCall, (DCpointer) p);
-      }
-      break;
-      default: return PyErr_Format( PyExc_RuntimeError, "unknown signature character '%c'", ch);
-    }
+			case DC_SIGCHAR_INT:
+			case DC_SIGCHAR_UINT:
+				if ( !DcPyInt_Check(po) )
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting an int", pos );
+				dcArgInt(gpCall, (DCint) DcPyInt_AS_LONG(po));
+				break;
+
+			case DC_SIGCHAR_LONG:
+			case DC_SIGCHAR_ULONG:
+				if ( !DcPyInt_Check(po) )
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting an int", pos );
+				dcArgLong(gpCall, (DClong) PyLong_AsLong(po));
+				break;
+
+			case DC_SIGCHAR_LONGLONG:
+			case DC_SIGCHAR_ULONGLONG:
+#if PY_MAJOR_VERSION < 3
+				if ( PyInt_Check(po) )
+					dcArgLongLong(gpCall, (DClonglong) PyInt_AS_LONG(po));
+				else
+#endif
+				if ( !PyLong_Check(po) )
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting " EXPECT_LONG_TYPE_STR, pos );
+				dcArgLongLong(gpCall, (DClonglong)PyLong_AsLongLong(po));
+				break;
+
+			case DC_SIGCHAR_FLOAT:
+				if (!PyFloat_Check(po))
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expeecting a float", pos );
+				dcArgFloat(gpCall, (float)PyFloat_AsDouble(po));
+				break;
+
+			case DC_SIGCHAR_DOUBLE:
+				if (!PyFloat_Check(po))
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expeecting a float", pos );
+				dcArgDouble(gpCall, PyFloat_AsDouble(po));
+				break;
 
-    ++pos; ++ptr;
-
-  }
-
-  if (pos != l) return PyErr_Format( PyExc_RuntimeError, "too many arguments");
-
-  if (ch == '\0') return PyErr_Format( PyExc_RuntimeError, "return value missing in signature");
-
-  ch = *++ptr;
+			case DC_SIGCHAR_POINTER:
+			{
+				DCpointer p;
+				if ( PyUnicode_Check(po) ) {
+					PyObject* bo = PyUnicode_AsEncodedString(po, "utf-8", "strict"); // Owned reference @@@
+					if (bo) {
+						p = PyBytes_AS_STRING(bo); // Borrowed pointer
+						//p = strdup(my_result);
+						//Py_DECREF(bo);
+					}
+				} else if ( DcPyString_Check(po) )
+					p = (DCpointer) DcPyString_AsString(po);
+#if PY_MAJOR_VERSION < 3
+				else if ( PyInt_Check(po) )
+					p = (DCpointer) PyInt_AS_LONG(po);
+#endif
+				else if ( PyLong_Check(po) )
+					p = (DCpointer) PyLong_AsVoidPtr(po);
+				else
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a promoting pointer-type (int,string)", pos ); //@@@ error message needs to specify python types
+				dcArgPointer(gpCall, p);
+			}
+			break;
 
-  switch(ch)
-  {
-    case DC_SIGCHAR_VOID:                                dcCallVoid    (gpCall, pfunc); Py_RETURN_NONE;
-    case DC_SIGCHAR_BOOL:      return Py_BuildValue("i", dcCallBool    (gpCall, pfunc));
-    case DC_SIGCHAR_CHAR:      return Py_BuildValue("b", dcCallChar    (gpCall, pfunc));
-    case DC_SIGCHAR_UCHAR:     return Py_BuildValue("B", dcCallChar    (gpCall, pfunc));
-    case DC_SIGCHAR_SHORT:     return Py_BuildValue("h", dcCallShort   (gpCall, pfunc));
-    case DC_SIGCHAR_USHORT:    return Py_BuildValue("H", dcCallShort   (gpCall, pfunc));
-    case DC_SIGCHAR_INT:       return Py_BuildValue("i", dcCallInt     (gpCall, pfunc));
-    case DC_SIGCHAR_UINT:      return Py_BuildValue("I", dcCallInt     (gpCall, pfunc));
-    case DC_SIGCHAR_LONG:      return Py_BuildValue("l", dcCallLong    (gpCall, pfunc));
-    case DC_SIGCHAR_ULONG:     return Py_BuildValue("k", dcCallLong    (gpCall, pfunc));
-    case DC_SIGCHAR_LONGLONG:  return Py_BuildValue("L", dcCallLongLong(gpCall, pfunc));
-    case DC_SIGCHAR_ULONGLONG: return Py_BuildValue("K", dcCallLongLong(gpCall, pfunc));
-    case DC_SIGCHAR_FLOAT:     return Py_BuildValue("f", dcCallFloat   (gpCall, pfunc));
-    case DC_SIGCHAR_DOUBLE:    return Py_BuildValue("d", dcCallDouble  (gpCall, pfunc));
-    case DC_SIGCHAR_STRING:    return Py_BuildValue("s", dcCallPointer (gpCall, pfunc));
-    case DC_SIGCHAR_POINTER:   return Py_BuildValue("n", dcCallPointer (gpCall, pfunc)); // @@@test, this used to be 'p' which doesn't exist, 'n' is for "Py_ssize_t"
-    default:                   return PyErr_Format(PyExc_RuntimeError, "invalid return type signature");
-  }
+			case DC_SIGCHAR_STRING:
+			{
+				const char* p;
+				if ( PyUnicode_Check(po) ) {
+					PyObject* bo = PyUnicode_AsEncodedString(po, "utf-8", "strict"); // Owned reference @@@
+					if (bo) {
+						p = PyBytes_AS_STRING(bo); // Borrowed pointer
+						//p = strdup(my_result);@@@
+						//Py_DECREF(bo);@@@
+					}
+				} else if ( DcPyString_Check(po) ) {
+					p = DcPyString_AsString(po);
+				} else {
+					return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a string", pos ); //@@@ error message needs to specify python types
+				}
+				dcArgPointer(gpCall, (DCpointer) p);
+			}
+			break;
+
+			default:
+				return PyErr_Format( PyExc_RuntimeError, "unknown signature character '%c'", ch);
+		}
+	}
+
+	if (pos != ts)
+		return PyErr_Format( PyExc_RuntimeError, "too many arguments");
+
+	if (ch == '\0')
+		return PyErr_Format( PyExc_RuntimeError, "return value missing in signature");
+
+
+	ch = *++ptr;
+	switch(ch)
+	{
+		case DC_SIGCHAR_VOID:                                dcCallVoid    (gpCall, pfunc); Py_RETURN_NONE;
+		case DC_SIGCHAR_BOOL:      return                    dcCallBool    (gpCall, pfunc)?Py_True:Py_False;
+		case DC_SIGCHAR_CHAR:      return Py_BuildValue("b", dcCallChar    (gpCall, pfunc));
+		case DC_SIGCHAR_UCHAR:     return Py_BuildValue("B", dcCallChar    (gpCall, pfunc));
+		case DC_SIGCHAR_SHORT:     return Py_BuildValue("h", dcCallShort   (gpCall, pfunc));
+		case DC_SIGCHAR_USHORT:    return Py_BuildValue("H", dcCallShort   (gpCall, pfunc));
+		case DC_SIGCHAR_INT:       return Py_BuildValue("i", dcCallInt     (gpCall, pfunc));
+		case DC_SIGCHAR_UINT:      return Py_BuildValue("I", dcCallInt     (gpCall, pfunc));
+		case DC_SIGCHAR_LONG:      return Py_BuildValue("l", dcCallLong    (gpCall, pfunc));
+		case DC_SIGCHAR_ULONG:     return Py_BuildValue("k", dcCallLong    (gpCall, pfunc));
+		case DC_SIGCHAR_LONGLONG:  return Py_BuildValue("L", dcCallLongLong(gpCall, pfunc));
+		case DC_SIGCHAR_ULONGLONG: return Py_BuildValue("K", dcCallLongLong(gpCall, pfunc));
+		case DC_SIGCHAR_FLOAT:     return Py_BuildValue("f", dcCallFloat   (gpCall, pfunc));
+		case DC_SIGCHAR_DOUBLE:    return Py_BuildValue("d", dcCallDouble  (gpCall, pfunc));
+		case DC_SIGCHAR_STRING:    return Py_BuildValue("s", dcCallPointer (gpCall, pfunc));
+		case DC_SIGCHAR_POINTER:   return Py_BuildValue("n", dcCallPointer (gpCall, pfunc)); // @@@test, this used to be 'p' which doesn't exist, 'n' is for "Py_ssize_t"
+		default:                   return PyErr_Format(PyExc_RuntimeError, "invalid return type signature");
+	}
 }
 
 
@@ -362,27 +393,27 @@
 PyMODINIT_FUNC
 PY_MOD_INIT_FUNC_NAME(void)
 {
-  static PyMethodDef pydcMethods[] = {
-    {"load", pydc_load, METH_VARARGS, "load library"},
-    {"find", pydc_find, METH_VARARGS, "find symbols"},
-    {"free", pydc_free, METH_VARARGS, "free library"},
-    {"call", pydc_call, METH_VARARGS, "call function"},
-    {NULL,NULL,0,NULL}
-  };
+	static PyMethodDef pydcMethods[] = {
+		{"load", pydc_load, METH_VARARGS, "load library"},
+		{"find", pydc_find, METH_VARARGS, "find symbols"},
+		{"free", pydc_free, METH_VARARGS, "free library"},
+		{"call", pydc_call, METH_VARARGS, "call function"},
+		{NULL,NULL,0,NULL}
+	};
 
-  PyObject* m;
+	PyObject* m;
 #if PY_MAJOR_VERSION >= 3
-  static struct PyModuleDef moddef = { PyModuleDef_HEAD_INIT, PYDC_MOD_NAME_STR, PYDC_MOD_DESC_STR, -1, pydcMethods, NULL, NULL, NULL, NULL };
-  m = PyModule_Create(&moddef);
+	static struct PyModuleDef moddef = { PyModuleDef_HEAD_INIT, PYDC_MOD_NAME_STR, PYDC_MOD_DESC_STR, -1, pydcMethods, NULL, NULL, NULL, NULL };
+	m = PyModule_Create(&moddef);
 #else
-  m = Py_InitModule3(PYDC_MOD_NAME_STR, pydcMethods, PYDC_MOD_DESC_STR);
+	m = Py_InitModule3(PYDC_MOD_NAME_STR, pydcMethods, PYDC_MOD_DESC_STR);
 #endif
 
-  if(m)
-    gpCall = dcNewCallVM(4096);
+	if(m)
+		gpCall = dcNewCallVM(4096); //@@@ one shared callvm for the entire module, this is not reentrant
 
 #if PY_MAJOR_VERSION >= 3
-  return m;
+	return m;
 #endif
 }