From e3e844472178eb68ac69261f90bc4ef1623d4809 Mon Sep 17 00:00:00 2001 From: Christopher Chavez Date: Sun, 16 Apr 2023 09:47:41 -0500 Subject: [PATCH 1/6] gh-103194: Fix value type handling for Tcl 8.7/9.0 --- Modules/_tkinter.c | 59 +++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 385a05932a77ed..760440178c0012 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -581,11 +581,44 @@ Tkapp_New(const char *screenName, const char *className, } v->OldBooleanType = Tcl_GetObjType("boolean"); - v->BooleanType = Tcl_GetObjType("booleanString"); - v->ByteArrayType = Tcl_GetObjType("bytearray"); + { + Tcl_Obj *value; + int boolValue; + + /* Tcl 8.5 booleanString type is not registered + and is renamed to boolean in Tcl 9.0. + Based on approach suggested at + https://core.tcl-lang.org/tcl/info/3bb3bcf2da5b */ + value = Tcl_NewStringObj("true", -1); + Tcl_GetBooleanFromObj(NULL, value, &boolValue); + //fprintf(stderr, "%s %p\n", value->typePtr->name, value->typePtr); + v->BooleanType = value->typePtr; + Tcl_DecrRefCount(value); + + // As suggested by TIP 484 + value = Tcl_NewIntObj(0); + //fprintf(stderr, "%s %p\n", value->typePtr->name, value->typePtr); + v->IntType = value->typePtr; + Tcl_DecrRefCount(value); + + /* Retrieve the 64-bit type, which is either "wideInt" or "int"; + the "wideInt" type is only available when the "int" type is 32-bit. */ + value = Tcl_NewWideIntObj( + /* Must use a value wider than 32-bit here, otherwise + Tcl_NewWideIntObj() could return a 32-bit "int". */ + (Tcl_WideInt)(1) << 32 + ); + //fprintf(stderr, "%s %p\n", value->typePtr->name, value->typePtr); + v->WideIntType = value->typePtr; + Tcl_DecrRefCount(value); + + // bytearray type is not registered in Tcl 9.0 + value = Tcl_NewByteArrayObj(NULL, 0); + //fprintf(stderr, "%s %p\n", value->typePtr->name, value->typePtr); + v->ByteArrayType = value->typePtr; + Tcl_DecrRefCount(value); + } v->DoubleType = Tcl_GetObjType("double"); - v->IntType = Tcl_GetObjType("int"); - v->WideIntType = Tcl_GetObjType("wideInt"); v->BignumType = Tcl_GetObjType("bignum"); v->ListType = Tcl_GetObjType("list"); v->ProcBodyType = Tcl_GetObjType("procbody"); @@ -1092,10 +1125,12 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) if (value->typePtr == tkapp->BooleanType || value->typePtr == tkapp->OldBooleanType) { + //fprintf(stderr, "fromBoolean\n"); return fromBoolean(tkapp, value); } if (value->typePtr == tkapp->ByteArrayType) { + //fprintf(stderr, "got bytearray\n"); int size; char *data = (char*)Tcl_GetByteArrayFromObj(value, &size); return PyBytes_FromStringAndSize(data, size); @@ -1105,16 +1140,9 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) return PyFloat_FromDouble(value->internalRep.doubleValue); } - if (value->typePtr == tkapp->IntType) { - long longValue; - if (Tcl_GetLongFromObj(interp, value, &longValue) == TCL_OK) - return PyLong_FromLong(longValue); - /* If there is an error in the long conversion, - fall through to wideInt handling. */ - } - if (value->typePtr == tkapp->IntType || value->typePtr == tkapp->WideIntType) { + //fprintf(stderr, "fromWideIntObj\n"); result = fromWideIntObj(tkapp, value); if (result != NULL || PyErr_Occurred()) return result; @@ -1165,13 +1193,6 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) return unicodeFromTclObj(value); } - if (tkapp->BooleanType == NULL && - strcmp(value->typePtr->name, "booleanString") == 0) { - /* booleanString type is not registered in Tcl */ - tkapp->BooleanType = value->typePtr; - return fromBoolean(tkapp, value); - } - if (tkapp->BignumType == NULL && strcmp(value->typePtr->name, "bignum") == 0) { /* bignum type is not registered in Tcl */ From df7c1b87cf506bbaee6410f942959db64476b4d6 Mon Sep 17 00:00:00 2001 From: Christopher Chavez Date: Sat, 22 Apr 2023 10:19:50 -0500 Subject: [PATCH 2/6] Try to recognize Tcl 8.7 utf32string --- Modules/_tkinter.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 760440178c0012..b03bdb8f08df82 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -312,6 +312,7 @@ typedef struct { const Tcl_ObjType *ListType; const Tcl_ObjType *ProcBodyType; const Tcl_ObjType *StringType; + const Tcl_ObjType *UTF32StringType; } TkappObject; #define Tkapp_Interp(v) (((TkappObject *) (v))->interp) @@ -617,6 +618,20 @@ Tkapp_New(const char *screenName, const char *className, //fprintf(stderr, "%s %p\n", value->typePtr->name, value->typePtr); v->ByteArrayType = value->typePtr; Tcl_DecrRefCount(value); + + /* Tcl 8.7 has an unregistered "utf32string" type, + which can be retrieved by running `string length` + on a string with at least 2 characters. + If the "utf32string" type is not present, + then this retrieves the "string" type. */ + Tcl_SetVar(v->interp, "mystring", "pq", 0); + Tcl_Eval(v->interp, "string length $mystring"); + value = Tcl_GetVar2Ex(v->interp, "mystring", NULL, 0); + v->UTF32StringType = value->typePtr; + fprintf(stderr, "%s %p\n", + value->typePtr ? value->typePtr->name : "pure string", + value->typePtr); + Tcl_UnsetVar(v->interp, "mystring", 0); } v->DoubleType = Tcl_GetObjType("double"); v->BignumType = Tcl_GetObjType("bignum"); @@ -1189,7 +1204,8 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) /* fall through: return tcl object. */ } - if (value->typePtr == tkapp->StringType) { + if (value->typePtr == tkapp->StringType || + value->typePtr == tkapp->UTF32StringType) { return unicodeFromTclObj(value); } From 804d0fd8942a538f0c716cbcbfbb205e46c08abc Mon Sep 17 00:00:00 2001 From: Christopher Chavez Date: Sat, 22 Apr 2023 16:04:47 -0500 Subject: [PATCH 3/6] "utf32string" is now registered --- Modules/_tkinter.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index b03bdb8f08df82..b8e58595501ecf 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -618,26 +618,13 @@ Tkapp_New(const char *screenName, const char *className, //fprintf(stderr, "%s %p\n", value->typePtr->name, value->typePtr); v->ByteArrayType = value->typePtr; Tcl_DecrRefCount(value); - - /* Tcl 8.7 has an unregistered "utf32string" type, - which can be retrieved by running `string length` - on a string with at least 2 characters. - If the "utf32string" type is not present, - then this retrieves the "string" type. */ - Tcl_SetVar(v->interp, "mystring", "pq", 0); - Tcl_Eval(v->interp, "string length $mystring"); - value = Tcl_GetVar2Ex(v->interp, "mystring", NULL, 0); - v->UTF32StringType = value->typePtr; - fprintf(stderr, "%s %p\n", - value->typePtr ? value->typePtr->name : "pure string", - value->typePtr); - Tcl_UnsetVar(v->interp, "mystring", 0); } v->DoubleType = Tcl_GetObjType("double"); v->BignumType = Tcl_GetObjType("bignum"); v->ListType = Tcl_GetObjType("list"); v->ProcBodyType = Tcl_GetObjType("procbody"); v->StringType = Tcl_GetObjType("string"); + v->UTF32StringType = Tcl_GetObjType("utf32string"); /* Delete the 'exit' command, which can screw things up */ Tcl_DeleteCommand(v->interp, "exit"); From 3c3393cca1fd2d75abb14f213dd2589a627ea5cd Mon Sep 17 00:00:00 2001 From: Christopher Chavez Date: Mon, 24 Apr 2023 00:10:02 -0500 Subject: [PATCH 4/6] Add blurb --- .../Library/2023-04-24-05-34-23.gh-issue-103194.GwBwWL.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-04-24-05-34-23.gh-issue-103194.GwBwWL.rst diff --git a/Misc/NEWS.d/next/Library/2023-04-24-05-34-23.gh-issue-103194.GwBwWL.rst b/Misc/NEWS.d/next/Library/2023-04-24-05-34-23.gh-issue-103194.GwBwWL.rst new file mode 100644 index 00000000000000..3f70168b81069e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-04-24-05-34-23.gh-issue-103194.GwBwWL.rst @@ -0,0 +1,4 @@ +Prepare Tkinter for C API changes in Tcl 8.7/9.0 to avoid +:class:`_tkinter.Tcl_Obj` being unexpectedly returned +instead of :class:`bool`, :class:`str`, +:class:`bytearray`, or :class:`int`. From 1db1da831099ae6d6dbcd1430a197e185bb5483c Mon Sep 17 00:00:00 2001 From: Christopher Chavez Date: Tue, 25 Apr 2023 10:45:46 -0500 Subject: [PATCH 5/6] Further refinements, cleanup Continue using Tcl_GetObjType() to retrieve obsolete "int" type on 8.7 for platforms with 32-bit long. 9.0 only has 64-bit "int"; no unregistered "wideInt" type to retrieve. --- Modules/_tkinter.c | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index b8e58595501ecf..583cdbd0e64e08 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -586,40 +586,32 @@ Tkapp_New(const char *screenName, const char *className, Tcl_Obj *value; int boolValue; - /* Tcl 8.5 booleanString type is not registered - and is renamed to boolean in Tcl 9.0. + /* Tcl 8.5 "booleanString" type is not registered + and is renamed to "boolean" in Tcl 9.0. Based on approach suggested at https://core.tcl-lang.org/tcl/info/3bb3bcf2da5b */ value = Tcl_NewStringObj("true", -1); Tcl_GetBooleanFromObj(NULL, value, &boolValue); - //fprintf(stderr, "%s %p\n", value->typePtr->name, value->typePtr); v->BooleanType = value->typePtr; Tcl_DecrRefCount(value); - // As suggested by TIP 484 - value = Tcl_NewIntObj(0); - //fprintf(stderr, "%s %p\n", value->typePtr->name, value->typePtr); - v->IntType = value->typePtr; - Tcl_DecrRefCount(value); - - /* Retrieve the 64-bit type, which is either "wideInt" or "int"; - the "wideInt" type is only available when the "int" type is 32-bit. */ - value = Tcl_NewWideIntObj( - /* Must use a value wider than 32-bit here, otherwise - Tcl_NewWideIntObj() could return a 32-bit "int". */ - (Tcl_WideInt)(1) << 32 - ); - //fprintf(stderr, "%s %p\n", value->typePtr->name, value->typePtr); - v->WideIntType = value->typePtr; - Tcl_DecrRefCount(value); - - // bytearray type is not registered in Tcl 9.0 + // "bytearray" type is not registered in Tcl 9.0 value = Tcl_NewByteArrayObj(NULL, 0); - //fprintf(stderr, "%s %p\n", value->typePtr->name, value->typePtr); v->ByteArrayType = value->typePtr; Tcl_DecrRefCount(value); } v->DoubleType = Tcl_GetObjType("double"); + /* TIP 484 suggests retrieving the "int" type without Tcl_GetObjType("int") + since it is no longer registered in Tcl 9.0. But even though Tcl 8.7 + only uses the "wideInt" type on platforms with 32-bit long, it still has + a registered "int" type, which FromObj() should recognize just in case. */ + v->IntType = Tcl_GetObjType("int"); + if (v->IntType == NULL) { + Tcl_Obj *value = Tcl_NewIntObj(0); + v->IntType = value->typePtr; + Tcl_DecrRefCount(value); + } + v->WideIntType = Tcl_GetObjType("wideInt"); v->BignumType = Tcl_GetObjType("bignum"); v->ListType = Tcl_GetObjType("list"); v->ProcBodyType = Tcl_GetObjType("procbody"); @@ -1127,12 +1119,10 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) if (value->typePtr == tkapp->BooleanType || value->typePtr == tkapp->OldBooleanType) { - //fprintf(stderr, "fromBoolean\n"); return fromBoolean(tkapp, value); } if (value->typePtr == tkapp->ByteArrayType) { - //fprintf(stderr, "got bytearray\n"); int size; char *data = (char*)Tcl_GetByteArrayFromObj(value, &size); return PyBytes_FromStringAndSize(data, size); @@ -1144,7 +1134,6 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) if (value->typePtr == tkapp->IntType || value->typePtr == tkapp->WideIntType) { - //fprintf(stderr, "fromWideIntObj\n"); result = fromWideIntObj(tkapp, value); if (result != NULL || PyErr_Occurred()) return result; From 7d63d0eb0f9ce39837b3a75506ea9799a0886ad9 Mon Sep 17 00:00:00 2001 From: Christopher Chavez Date: Sun, 2 Jul 2023 22:19:00 -0500 Subject: [PATCH 6/6] Conform to PEP 7 for added brace --- Modules/_tkinter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 9389fbabdb64ac..8dd9f5919fb135 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -1199,7 +1199,8 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) } if (value->typePtr == tkapp->StringType || - value->typePtr == tkapp->UTF32StringType) { + value->typePtr == tkapp->UTF32StringType) + { return unicodeFromTclObj(value); }