--- a/tclpython.c 2006-03-07 16:28:03.000000000 +0300 +++ b/tclpython.c 2014-05-06 23:33:41.713623943 +0400 @@ -19,13 +19,83 @@ $ cc -fpic -I/usr/local/include/tcltk/tcl8.3 -c tclthread.c $ ld -o tclpython.so -Bshareable -L/usr/X11R6/lib -L/usr/local/lib -L/usr/local/share/python/config tclpython.o tclthread.o -lpython -lutil -lreadline -ltermcap -lcrypt -lgmp -lgdbm -lpq -lz -ltcl83 -ltk83 -lX11 +Patched for Python 3 with respect to https://github.com/facebook/fbthrift/blob/master/thrift/lib/py/protocol/fastbinary.c + */ #include #include -#include + +#if PY_MAJOR_VERSION >= 3 + #define PyInt_FromLong PyLong_FromLong + #define PyInt_AsLong PyLong_AsLong + #define PyString_FromStringAndSize PyBytes_FromStringAndSize +#else + #include +#endif + #include "tclpython.h" +// Mostly copied from cStringIO.c +#if PY_MAJOR_VERSION >= 3 + +/** io module in python3. */ +static PyObject* Python3IO; + +typedef struct { + PyObject_HEAD + char *buf; + Py_ssize_t pos, string_size; +} IOobject; + +#define IOOOBJECT(O) ((IOobject*)(O)) + +static int +IO__opencheck(IOobject *self) { + if (!self->buf) { + PyErr_SetString(PyExc_ValueError, + "I/O operation on closed file"); + return 0; + } + return 1; +} + +static PyObject * +IO_cgetval(PyObject *self) { + if (!IO__opencheck(IOOOBJECT(self))) return NULL; + assert(IOOOBJECT(self)->pos >= 0); + return PyBytes_FromStringAndSize(((IOobject*)self)->buf, + ((IOobject*)self)->pos); +} +#endif + +/* -- PYTHON MODULE SETUP STUFF --- */ + +static PyObject *pythonTclEvaluate(PyObject *self, PyObject *args); + +static PyMethodDef tclMethods[] = { + {"eval", pythonTclEvaluate, METH_VARARGS, "Evaluate a Tcl script."}, + {0, 0, 0, 0} /* sentinel */ +}; + +#if PY_MAJOR_VERSION >= 3 +struct module_state { + PyObject *error; +}; + +static struct PyModuleDef TclModuleDef = { + PyModuleDef_HEAD_INIT, + "tcl", + NULL, + sizeof(struct module_state), + tclMethods, + NULL, + NULL, + NULL, + NULL +}; +#endif + #ifndef WIN32 /* George Petasis, 21 Feb 2006: * The following check cannot be handled correctly @@ -66,13 +136,13 @@ static int pythonInterpreter(ClientData clientData, Tcl_Interp *interpreter, int numberOfArguments, Tcl_Obj * CONST arguments[]) { - int identifier; + intptr_t identifier; PyObject *output; PyObject *message; PyObject *result; PyObject *globals; char *string = 0; - int length; + Py_ssize_t length; Tcl_Obj *object; struct Tcl_HashEntry *entry; unsigned evaluate; @@ -111,12 +181,22 @@ /* choose start token depending on whether this is an evaluation or an execution: */ result = PyRun_String(Tcl_GetString(arguments[2]), (evaluate? Py_eval_input: Py_file_input), globals, globals); if (result == 0) { /* an error occured */ +#if PY_MAJOR_VERSION >= 3 + output = PyObject_CallMethod(Python3IO, "BytesIO", "()"); +#else output = PycStringIO->NewOutput(1024); /* use a reasonable initial size but big enough to handle most cases */ - PySys_SetObject("stderr", output); /* capture all interpreter error output */ +#endif + PySys_SetObject("sys.stderr", output); /* capture all interpreter error output */ PyErr_Print(); /* so that error is printed on standard error, redirected above */ +#if PY_MAJOR_VERSION >= 3 + message = IO_cgetval(output); + string = PyBytes_AsString(message); + length = (string == NULL) ? 0 : strlen(string); +#else message = PycStringIO->cgetvalue(output); string = PyString_AsString(message); length = PyString_Size(message); +#endif if ((length > 0) && (string[length - 1] == '\n')) length--; /* eventually remove trailing new line character */ object = Tcl_NewObj(); Tcl_AppendStringsToObj(object, Tcl_GetString(arguments[0]), ": ", 0); /* identify interpreter in error */ @@ -124,7 +204,11 @@ Py_DECREF(output); } else { if (evaluate) { +#if PY_MAJOR_VERSION >= 3 + string = PyUnicode_AsUTF8(PyObject_Str(result)); +#else string = PyString_AsString(PyObject_Str(result)); +#endif object = Tcl_NewStringObj(string, -1); /* return evaluation result */ } else /* execute */ object = Tcl_NewObj(); /* always return an empty result or an error */ @@ -139,9 +223,9 @@ Tcl_Interp *tclInterpreter(CONST char *name) /* public function for use in extensions to this extension */ { - int identifier; + intptr_t identifier; - if ((sscanf(name, "tcl%u", &identifier) == 0) || (identifier != 0)) { + if ((sscanf(name, "tcl%lu", &identifier) == 0) || (identifier != 0)) { return 0; /* invalid name */ } else { return mainInterpreter; /* sole available interpreter */ @@ -188,14 +272,9 @@ return Py_BuildValue("s", result); } -static PyMethodDef tclMethods[] = { - {"eval", pythonTclEvaluate, METH_VARARGS, "Evaluate a Tcl script."}, - {0, 0, 0, 0} /* sentinel */ -}; - static int newInterpreter(Tcl_Interp *interpreter) { - int identifier; + intptr_t identifier; Tcl_Obj *object; int created; #ifdef WITH_THREAD @@ -214,19 +293,31 @@ return TCL_ERROR; } else { Py_Initialize(); /* initialize main interpreter */ +#if PY_MAJOR_VERSION >= 3 + Python3IO = PyImport_ImportModule("io"); +#else PycString_IMPORT; +#endif } Tcl_SetHashValue(Tcl_CreateHashEntry(&threadStates, (ClientData)identifier, &created), 0); #else if (existingInterpreters == 0) { Py_Initialize(); /* initialize main interpreter */ PyEval_InitThreads(); /* initialize and acquire the global interpreter lock */ +#if PY_MAJOR_VERSION >= 3 + Python3IO = PyImport_ImportModule("io"); +#else PycString_IMPORT; +#endif globalState = PyThreadState_Swap(0); /* save the global thread */ } else { PyEval_AcquireLock(); /* needed in order to be able to create a new interpreter */ } +#if PY_MAJOR_VERSION >= 3 + if (Python3IO == 0) { /* make sure string input/output is properly initialized */ +#else if (PycStringIO == 0) { /* make sure string input/output is properly initialized */ +#endif Tcl_SetResult(interpreter, "fatal error: could not initialize Python string input/output module", TCL_STATIC); return TCL_ERROR; } @@ -250,7 +341,11 @@ newIdentifier++; #endif existingInterpreters++; +#if PY_MAJOR_VERSION >= 3 + tcl = PyModule_Create(&TclModuleDef); +#else tcl = Py_InitModule("tcl", tclMethods); /* add a new 'tcl' module to the python interpreter */ +#endif Py_INCREF(tcl); PyModule_AddObject(PyImport_AddModule("__builtin__"), "tcl", tcl); return TCL_OK; @@ -260,7 +355,7 @@ { int index; char *name; - int identifier; + intptr_t identifier; struct Tcl_HashEntry *entry; Tcl_Obj *object; #ifdef WITH_THREAD @@ -270,7 +365,7 @@ for (index = 0; index < numberOfArguments; index++) { name = Tcl_GetString(arguments[index]); /* interpreter name is "pythonN" */ entry = 0; - if (sscanf(name, "python%u", &identifier) == 1) { + if (sscanf(name, "python%lu", &identifier) == 1) { identifier = atoi(name + 6); entry = Tcl_FindHashEntry(&threadStates, (ClientData)identifier); }