From 67034b91dcd1ea95f846f3fe19c2324d4aaca715 Mon Sep 17 00:00:00 2001 From: Jonathan Helmus Date: Tue, 9 Dec 2025 10:08:28 -0600 Subject: [PATCH] BUG: restore tcl/tk patch on python 3.14+ Restore patch used to find Tcl/Tk libraries relative to the base prefix on Python 3.14+. This patches was accidentally removed in #845. Rewrite the conditionals in the patch block to make it more obvious what patches are applied for each python version. closes #913 --- cpython-unix/build-cpython.sh | 10 ++- cpython-unix/patch-tkinter-3.14.patch | 117 ++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 cpython-unix/patch-tkinter-3.14.patch diff --git a/cpython-unix/build-cpython.sh b/cpython-unix/build-cpython.sh index a971b0570..8504fb8e3 100755 --- a/cpython-unix/build-cpython.sh +++ b/cpython-unix/build-cpython.sh @@ -188,13 +188,15 @@ fi # On Windows, CPython looks for the Tcl/Tk libraries relative to the base prefix, # which we want. But on Unix, it doesn't. This patch applies similar behavior on Unix, # thereby ensuring that the Tcl/Tk libraries are found in the correct location. -if [ "${PYTHON_MAJMIN_VERSION}" = "3.13" ]; then +if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" ]; then + patch -p1 -i ${ROOT}/patch-tkinter-3.14.patch +elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_13}" ]; then patch -p1 -i ${ROOT}/patch-tkinter-3.13.patch -elif [ "${PYTHON_MAJMIN_VERSION}" = "3.12" ]; then +elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then patch -p1 -i ${ROOT}/patch-tkinter-3.12.patch -elif [ "${PYTHON_MAJMIN_VERSION}" = "3.11" ]; then +elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]; then patch -p1 -i ${ROOT}/patch-tkinter-3.11.patch -elif [ "${PYTHON_MAJMIN_VERSION}" = "3.10" ]; then +elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_10}" ]; then patch -p1 -i ${ROOT}/patch-tkinter-3.10.patch fi diff --git a/cpython-unix/patch-tkinter-3.14.patch b/cpython-unix/patch-tkinter-3.14.patch new file mode 100644 index 000000000..3e99eb38a --- /dev/null +++ b/cpython-unix/patch-tkinter-3.14.patch @@ -0,0 +1,117 @@ +diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c +index e153047b778..02f5d12db1a 100644 +--- a/Modules/_tkinter.c ++++ b/Modules/_tkinter.c +@@ -115,6 +115,7 @@ Copyright (C) 1994 Steen Lumholt. + #ifdef MS_WINDOWS + #include + #define WAIT_FOR_STDIN ++#endif + + static PyObject * + _get_tcl_lib_path() +@@ -132,6 +133,7 @@ _get_tcl_lib_path() + return NULL; + } + ++#ifdef MS_WINDOWS + /* Check expected location for an installed Python first */ + tcl_library_path = PyUnicode_FromString("\\tcl\\tcl" TCL_VERSION); + if (tcl_library_path == NULL) { +@@ -169,11 +171,31 @@ _get_tcl_lib_path() + tcl_library_path = NULL; + #endif + } ++#else ++ /* Check expected location for an installed Python first */ ++ tcl_library_path = PyUnicode_FromString("/lib/tcl" TCL_VERSION); ++ if (tcl_library_path == NULL) { ++ return NULL; ++ } ++ tcl_library_path = PyUnicode_Concat(prefix, tcl_library_path); ++ if (tcl_library_path == NULL) { ++ return NULL; ++ } ++ stat_return_value = _Py_stat(tcl_library_path, &stat_buf); ++ if (stat_return_value == -2) { ++ return NULL; ++ } ++ if (stat_return_value == -1) { ++ /* install location doesn't exist, reset errno and leave Tcl ++ to its own devices */ ++ errno = 0; ++ tcl_library_path = NULL; ++ } ++#endif + already_checked = 1; + } + return tcl_library_path; + } +-#endif /* MS_WINDOWS */ + + /* The threading situation is complicated. Tcl is not thread-safe, except + when configured with --enable-threads. +@@ -822,6 +844,30 @@ Tkapp_New(const char *screenName, const char *className, + + ret = GetEnvironmentVariableW(L"TCL_LIBRARY", NULL, 0); + if (!ret && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { ++ str_path = _get_tcl_lib_path(); ++ if (str_path == NULL && PyErr_Occurred()) { ++ return NULL; ++ } ++ if (str_path != NULL) { ++ utf8_path = PyUnicode_AsUTF8String(str_path); ++ if (utf8_path == NULL) { ++ return NULL; ++ } ++ Tcl_SetVar(v->interp, ++ "tcl_library", ++ PyBytes_AS_STRING(utf8_path), ++ TCL_GLOBAL_ONLY); ++ Py_DECREF(utf8_path); ++ } ++ } ++ } ++#else ++ { ++ const char *env_val = getenv("TCL_LIBRARY"); ++ if (!env_val) { ++ PyObject *str_path; ++ PyObject *utf8_path; ++ + str_path = _get_tcl_lib_path(); + if (str_path == NULL && PyErr_Occurred()) { + return NULL; +@@ -3631,7 +3677,32 @@ PyInit__tkinter(void) + PyMem_Free(wcs_path); + } + #else ++ int set_var = 0; ++ PyObject *str_path; ++ char *path; ++ ++ if (!getenv("TCL_LIBRARY")) { ++ str_path = _get_tcl_lib_path(); ++ if (str_path == NULL && PyErr_Occurred()) { ++ Py_DECREF(m); ++ return NULL; ++ } ++ if (str_path != NULL) { ++ path = PyUnicode_AsUTF8(str_path); ++ if (path == NULL) { ++ Py_DECREF(m); ++ return NULL; ++ } ++ setenv("TCL_LIBRARY", path, 1); ++ set_var = 1; ++ } ++ } ++ + Tcl_FindExecutable(PyBytes_AS_STRING(cexe)); ++ ++ if (set_var) { ++ unsetenv("TCL_LIBRARY"); ++ } + #endif /* MS_WINDOWS */ + } + Py_XDECREF(cexe);