From ffad633af9d93ac7bd7c08b3154e8e5f71b920ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Tue, 26 Nov 2002 09:28:05 +0000 Subject: [PATCH] Patch #518625: Return objects in Tkinter. --- Lib/lib-tk/Tkinter.py | 3 + Misc/NEWS | 5 + Modules/_tkinter.c | 333 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 335 insertions(+), 6 deletions(-) diff --git a/Lib/lib-tk/Tkinter.py b/Lib/lib-tk/Tkinter.py index b5c07cb3c6f..f222007ea7d 100644 --- a/Lib/lib-tk/Tkinter.py +++ b/Lib/lib-tk/Tkinter.py @@ -45,6 +45,8 @@ try: except ImportError: _MacOS = None +want_objects = 1 + TkVersion = float(_tkinter.TK_VERSION) TclVersion = float(_tkinter.TCL_VERSION) @@ -1521,6 +1523,7 @@ class Tk(Misc, Wm): if ext not in ('.py', '.pyc', '.pyo'): baseName = baseName + ext self.tk = _tkinter.create(screenName, baseName, className) + self.tk.wantobjects(want_objects) if _MacOS and hasattr(_MacOS, 'SchedParams'): # Disable event scanning except for Command-Period _MacOS.SchedParams(1, 0) diff --git a/Misc/NEWS b/Misc/NEWS index 2f786945a35..9e1a8127752 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -312,6 +312,11 @@ Core and builtins Extension modules ----------------- +- _tkinter now returns Tcl objects, instead of strings. Objects which + have Python equivalents are converted to Python objects, other objects + are wrapped. This can be configured through the wantobjects method, + or Tkinter.want_objects. + - The PyBSDDB wrapper around the Sleepycat Berkely DB library has been added as the package bsddb. The traditional bsddb module is still available in source code, but not built automatically anymore, and diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 3dface6a4e5..b74641e07ea 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -50,9 +50,11 @@ Copyright (C) 1994 Steen Lumholt. #ifdef TK_FRAMEWORK #include +#include #include #else #include +#include #include #endif @@ -219,6 +221,7 @@ static PyTypeObject Tkapp_Type; typedef struct { PyObject_HEAD Tcl_Interp *interp; + int want_objects; } TkappObject; #define Tkapp_Check(v) ((v)->ob_type == &Tkapp_Type) @@ -424,6 +427,67 @@ Split(char *list) return v; } +/* In some cases, Tcl will still return strings that are supposed to be + lists. SplitObj walks through a nested tuple, finding string objects that + need to be split. */ + +PyObject * +SplitObj(PyObject *arg) +{ + if (PyTuple_Check(arg)) { + int i, size; + PyObject *elem, *newelem, *result; + + size = PyTuple_Size(arg); + result = NULL; + /* Recursively invoke SplitObj for all tuple items. + If this does not return a new object, no action is + needed. */ + for(i = 0; i < size; i++) { + elem = PyTuple_GetItem(arg, i); + newelem = SplitObj(elem); + if (!newelem) { + Py_XDECREF(result); + return NULL; + } + if (!result) { + int k; + if (newelem == elem) { + Py_DECREF(newelem); + continue; + } + result = PyTuple_New(size); + if (!result) + return NULL; + for(k = 0; k < i; k++) { + elem = PyTuple_GetItem(arg, k); + Py_INCREF(elem); + PyTuple_SetItem(result, k, elem); + } + } + PyTuple_SetItem(result, i, newelem); + } + if (result) + return result; + /* Fall through, returning arg. */ + } + else if (PyString_Check(arg)) { + int argc; + char **argv; + char *list = PyString_AsString(arg); + + if (Tcl_SplitList((Tcl_Interp *)NULL, list, &argc, &argv) != TCL_OK) { + Py_INCREF(arg); + return arg; + } + Tcl_Free(FREECAST argv); + if (argc > 1) + return Split(PyString_AsString(arg)); + /* Fall through, returning arg. */ + } + Py_INCREF(arg); + return arg; +} /**** Tkapp Object ****/ @@ -458,7 +522,8 @@ static void EnableEventHook(void); /* Forward */ static void DisableEventHook(void); /* Forward */ static TkappObject * -Tkapp_New(char *screenName, char *baseName, char *className, int interactive) +Tkapp_New(char *screenName, char *baseName, char *className, + int interactive, int want_objects) { TkappObject *v; char *argv0; @@ -468,6 +533,7 @@ Tkapp_New(char *screenName, char *baseName, char *className, int interactive) return NULL; v->interp = Tcl_CreateInterp(); + v->want_objects = want_objects; #if defined(macintosh) /* This seems to be needed */ @@ -513,6 +579,104 @@ Tkapp_New(char *screenName, char *baseName, char *className, int interactive) /** Tcl Eval **/ +typedef struct { + PyObject_HEAD + Tcl_Obj *value; +} PyTclObject; + +staticforward PyTypeObject PyTclObject_Type; +#define PyTclObject_Check(v) ((v)->ob_type == &PyTclObject_Type) + +static PyObject * +newPyTclObject(Tcl_Obj *arg) +{ + PyTclObject *self; + self = PyObject_New(PyTclObject, &PyTclObject_Type); + if (self == NULL) + return NULL; + Tcl_IncrRefCount(arg); + self->value = arg; + return (PyObject*)self; +} + +static void +PyTclObject_dealloc(PyTclObject *self) +{ + Tcl_DecrRefCount(self->value); + PyObject_Del(self); +} + +static PyObject * +PyTclObject_str(PyTclObject *self) +{ + return PyString_FromString(Tcl_GetString(self->value)); +} + +static PyObject * +PyTclObject_repr(PyTclObject *self) +{ + char buf[50]; + PyOS_snprintf(buf, 50, "<%s object at 0x%.8x>", + self->value->typePtr->name, (int)self->value); + return PyString_FromString(buf); +} + +static PyObject* +get_typename(PyTclObject* obj, void* ignored) +{ + return PyString_FromString(obj->value->typePtr->name); +} + +static PyGetSetDef PyTclObject_getsetlist[] = { + {"typename", (getter)get_typename, NULL, "name of the Tcl type"}, + {0}, +}; + +statichere PyTypeObject PyTclObject_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_tkinter.Tcl_Obj", /*tp_name*/ + sizeof(PyTclObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)PyTclObject_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc)PyTclObject_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + (reprfunc)PyTclObject_str, /*tp_str*/ + PyObject_GenericGetAttr,/*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + 0, /*tp_methods*/ + 0, /*tp_members*/ + PyTclObject_getsetlist, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + static Tcl_Obj* AsObj(PyObject *value) { @@ -570,6 +734,11 @@ AsObj(PyObject *value) } #endif + else if(PyTclObject_Check(value)) { + Tcl_Obj *v = ((PyTclObject*)value)->value; + Tcl_IncrRefCount(v); + return v; + } else { PyObject *v = PyObject_Str(value); if (!v) @@ -580,6 +749,99 @@ AsObj(PyObject *value) } } +static PyObject* +FromObj(PyObject* tkapp, Tcl_Obj *value) +{ + PyObject *result = NULL; + + if (value->typePtr == NULL) + return PyString_FromStringAndSize(value->bytes, value->length); + + if (value->typePtr == &tclBooleanType) { + result = value->internalRep.longValue ? Py_True : Py_False; + Py_INCREF(result); + return result; + } + + if (value->typePtr == &tclByteArrayType) { + int size; + char *data = Tcl_GetByteArrayFromObj(value, &size); + return PyString_FromStringAndSize(data, size); + } + + if (value->typePtr == &tclDoubleType) { + return PyFloat_FromDouble(value->internalRep.doubleValue); + } + + if (value->typePtr == &tclIntType) { + return PyInt_FromLong(value->internalRep.longValue); + } + + if (value->typePtr == &tclListType) { + int size; + int i, status; + PyObject *elem; + Tcl_Obj *tcl_elem; + + status = Tcl_ListObjLength(Tkapp_Interp(tkapp), value, &size); + if (status == TCL_ERROR) + return Tkinter_Error(tkapp); + result = PyTuple_New(size); + if (!result) + return NULL; + for (i = 0; i < size; i++) { + status = Tcl_ListObjIndex(Tkapp_Interp(tkapp), + value, i, &tcl_elem); + if (status == TCL_ERROR) { + Py_DECREF(result); + return Tkinter_Error(tkapp); + } + elem = FromObj(tkapp, tcl_elem); + if (!elem) { + Py_DECREF(result); + return NULL; + } + PyTuple_SetItem(result, i, elem); + } + return result; + } + + if (value->typePtr == &tclProcBodyType) { + // fall through: return tcl object + } + + if (value->typePtr == &tclStringType) { +#ifdef Py_USING_UNICODE +#ifdef Py_UNICODE_WIDE + PyObject *result; + int size; + Tcl_UniChar *input; + Py_UNICODE *output; + + size = Tcl_GetCharLength(value); + result = PyUnicode_FromUnicode(NULL, size); + if (!result) + return NULL; + input = Tcl_GetUnicode(value); + output = PyUnicode_AS_UNICODE(result); + while (size--) + *output++ = *input++; + return result; +#else + return PyUnicode_FromUnicode(Tcl_GetUnicode(value), + Tcl_GetCharLength(value)); +#endif +#else + int size; + char *c; + c = Tcl_GetStringFromObj(value, &size); + return PyString_FromStringAndSize(c, size); +#endif + } + + return newPyTclObject(value); +} + static PyObject * Tkapp_Call(PyObject *self, PyObject *args) { @@ -639,9 +901,15 @@ Tkapp_Call(PyObject *self, PyObject *args) ENTER_OVERLAP if (i == TCL_ERROR) Tkinter_Error(self); - else { - /* We could request the object result here, but doing - so would confuse applications that expect a string. */ + else if(((TkappObject*)self)->want_objects) { + Tcl_Obj *value = Tcl_GetObjResult(interp); + /* Not sure whether the IncrRef is necessary, but something + may overwrite the interpreter result while we are + converting it. */ + Tcl_IncrRefCount(value); + res = FromObj(self, value); + Tcl_DecrRefCount(value); + } else { const char *s = Tcl_GetStringResult(interp); const char *p = s; @@ -964,6 +1232,13 @@ Tkapp_GetInt(PyObject *self, PyObject *args) char *s; int v; + if (PyTuple_Size(args) == 1) { + PyObject* o = PyTuple_GetItem(args, 0); + if (PyInt_Check(o)) { + Py_INCREF(o); + return o; + } + } if (!PyArg_ParseTuple(args, "s:getint", &s)) return NULL; if (Tcl_GetInt(Tkapp_Interp(self), s, &v) == TCL_ERROR) @@ -977,6 +1252,13 @@ Tkapp_GetDouble(PyObject *self, PyObject *args) char *s; double v; + if (PyTuple_Size(args) == 1) { + PyObject *o = PyTuple_GetItem(args, 0); + if (PyFloat_Check(o)) { + Py_INCREF(o); + return o; + } + } if (!PyArg_ParseTuple(args, "s:getdouble", &s)) return NULL; if (Tcl_GetDouble(Tkapp_Interp(self), s, &v) == TCL_ERROR) @@ -990,6 +1272,13 @@ Tkapp_GetBoolean(PyObject *self, PyObject *args) char *s; int v; + if (PyTuple_Size(args) == 1) { + PyObject *o = PyTuple_GetItem(args, 0); + if (PyInt_Check(o)) { + Py_INCREF(o); + return o; + } + } if (!PyArg_ParseTuple(args, "s:getboolean", &s)) return NULL; if (Tcl_GetBoolean(Tkapp_Interp(self), s, &v) == TCL_ERROR) @@ -1093,6 +1382,13 @@ Tkapp_SplitList(PyObject *self, PyObject *args) PyObject *v; int i; + if (PyTuple_Size(args) == 1) { + v = PyTuple_GetItem(args, 0); + if (PyTuple_Check(v)) { + Py_INCREF(v); + return v; + } + } if (!PyArg_ParseTuple(args, "et:splitlist", "utf-8", &list)) return NULL; @@ -1121,6 +1417,13 @@ Tkapp_Split(PyObject *self, PyObject *args) { char *list; + if (PyTuple_Size(args) == 1) { + PyObject* o = PyTuple_GetItem(args, 0); + if (PyTuple_Check(o)) { + o = SplitObj(o); + return o; + } + } if (!PyArg_ParseTuple(args, "et:split", "utf-8", &list)) return NULL; return Split(list); @@ -1660,11 +1963,26 @@ Tkapp_InterpAddr(PyObject *self, PyObject *args) } +static PyObject * +Tkapp_WantObjects(PyObject *self, PyObject *args) +{ + + int want_objects; + if (!PyArg_ParseTuple(args, "i:wantobjects", &want_objects)) + return NULL; + ((TkappObject*)self)->want_objects = want_objects; + + Py_INCREF(Py_None); + return Py_None; +} + + /**** Tkapp Method List ****/ static PyMethodDef Tkapp_methods[] = { + {"wantobjects", Tkapp_WantObjects, METH_VARARGS}, {"call", Tkapp_Call, METH_OLDARGS}, {"globalcall", Tkapp_GlobalCall, METH_OLDARGS}, {"eval", Tkapp_Eval, METH_VARARGS}, @@ -1861,6 +2179,7 @@ Tkinter_Create(PyObject *self, PyObject *args) char *baseName = NULL; char *className = NULL; int interactive = 0; + int want_objects = 0; baseName = strrchr(Py_GetProgramName(), '/'); if (baseName != NULL) @@ -1871,11 +2190,11 @@ Tkinter_Create(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "|zssi:create", &screenName, &baseName, &className, - &interactive)) + &interactive, &want_objects)) return NULL; return (PyObject *) Tkapp_New(screenName, baseName, className, - interactive); + interactive, want_objects); } static PyMethodDef moduleMethods[] = @@ -2045,6 +2364,8 @@ init_tkinter(void) Tktt_Type.ob_type = &PyType_Type; PyDict_SetItemString(d, "TkttType", (PyObject *)&Tktt_Type); + PyTclObject_Type.ob_type = &PyType_Type; + PyDict_SetItemString(d, "Tcl_Obj", (PyObject *)&PyTclObject_Type); #ifdef TK_AQUA /* Tk_MacOSXSetupTkNotifier must be called before Tcl's subsystems