gh-103194: Fix Tkinter’s Tcl value type handling for Tcl 8.7/9.0 (GH-103846)

Some of standard Tcl types were renamed, removed, or no longer
registered in Tcl 8.7/9.0. This change fixes automatic conversion of Tcl
values to Python values to avoid returning a Tcl_Obj where the primary
Python types (int, bool, str, bytes) were returned in older Tcl.
This commit is contained in:
Christopher Chavez 2024-05-31 03:23:53 -05:00 committed by GitHub
parent b278c723d7
commit 94e9585e99
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 36 additions and 18 deletions

View File

@ -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`.

View File

@ -318,6 +318,7 @@ typedef struct {
const Tcl_ObjType *BignumType;
const Tcl_ObjType *ListType;
const Tcl_ObjType *StringType;
const Tcl_ObjType *UTF32StringType;
} TkappObject;
#define Tkapp_Interp(v) (((TkappObject *) (v))->interp)
@ -588,14 +589,40 @@ 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);
v->BooleanType = value->typePtr;
Tcl_DecrRefCount(value);
// "bytearray" type is not registered in Tcl 9.0
value = Tcl_NewByteArrayObj(NULL, 0);
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->StringType = Tcl_GetObjType("string");
v->UTF32StringType = Tcl_GetObjType("utf32string");
/* Delete the 'exit' command, which can screw things up */
Tcl_DeleteCommand(v->interp, "exit");
@ -1124,14 +1151,6 @@ 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) {
result = fromWideIntObj(tkapp, value);
@ -1176,17 +1195,12 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value)
return result;
}
if (value->typePtr == tkapp->StringType) {
if (value->typePtr == tkapp->StringType ||
value->typePtr == tkapp->UTF32StringType)
{
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 */