]> granicus.if.org Git - python/commitdiff
Issue #20035: Reimplement tkinter._fix module as a C function.
authorZachary Ware <zachary.ware@gmail.com>
Fri, 22 May 2015 16:36:53 +0000 (11:36 -0500)
committerZachary Ware <zachary.ware@gmail.com>
Fri, 22 May 2015 16:36:53 +0000 (11:36 -0500)
The new private C function makes no permanent changes to the environment
and is #ifdef'd out on non-Windows platforms.

12 files changed:
Doc/whatsnew/3.5.rst
Lib/test/test_tcl.py
Lib/test/test_tk.py
Lib/test/test_ttk_guionly.py
Lib/test/test_ttk_textonly.py
Lib/tkinter/__init__.py
Lib/tkinter/_fix.py [deleted file]
Misc/NEWS
Modules/_tkinter.c
PCbuild/_tkinter.vcxproj
Tools/buildbot/test-amd64.bat
Tools/buildbot/test.bat

index b2571ae45e0fd0bfb4bd4850443f26fb9da87ce8..a1494172e7bd6d1fc04d09b9379860ff0123f42e 100644 (file)
@@ -1,4 +1,4 @@
-****************************
+****************************
   What's New In Python 3.5
 ****************************
 
@@ -659,6 +659,14 @@ time
 * The :func:`time.monotonic` function is now always available.  (Contributed by
   Victor Stinner in :issue:`22043`.)
 
+tkinter
+-------
+
+* The :module:`tkinter._fix` module used for setting up the Tcl/Tk environment
+  on Windows has been replaced by a private function in the :module:`_tkinter`
+  module which makes no permanent changes to environment variables.
+  (Contributed by Zachary Ware in :issue:`20035`.)
+
 types
 -----
 
index b612517941ff45ed6d9993ea1274c2543bdcd517..5be645a32ebc367d0650a59c72f2346cea28bc2e 100644 (file)
@@ -7,9 +7,7 @@ from test import support
 # Skip this test if the _tkinter module wasn't built.
 _tkinter = support.import_module('_tkinter')
 
-# Make sure tkinter._fix runs to set up the environment
-tkinter = support.import_fresh_module('tkinter')
-
+import tkinter
 from tkinter import Tcl
 from _tkinter import TclError
 
index 62729f0f32495418891bfdc4fa683176741d9b93..48cefd92e121627d36c5e478de4c6e5d8d7193af 100644 (file)
@@ -2,9 +2,6 @@ from test import support
 # Skip test if _tkinter wasn't built.
 support.import_module('_tkinter')
 
-# Make sure tkinter._fix runs to set up the environment
-support.import_fresh_module('tkinter')
-
 # Skip test if tk cannot be initialized.
 support.requires('gui')
 
index fcdedac34dc6b4bc1b53d08abb7320963da2cea2..490e72337f24c20576953e6fae72667ec5c17d85 100644 (file)
@@ -5,12 +5,10 @@ from test import support
 # Skip this test if _tkinter wasn't built.
 support.import_module('_tkinter')
 
-# Make sure tkinter._fix runs to set up the environment
-tkinter = support.import_fresh_module('tkinter')
-
 # Skip test if tk cannot be initialized.
 support.requires('gui')
 
+import tkinter
 from _tkinter import TclError
 from tkinter import ttk
 from tkinter.test import runtktests
index 1cfeb15d2a0f4f106f7f8cdee23e549423fa1015..566fc9d09a04a3cb6715c10b31b02bdf5cddd057 100644 (file)
@@ -4,9 +4,6 @@ from test import support
 # Skip this test if _tkinter does not exist.
 support.import_module('_tkinter')
 
-# Make sure tkinter._fix runs to set up the environment
-support.import_fresh_module('tkinter')
-
 from tkinter.test import runtktests
 
 def test_main():
index 196809b7b71c38c23988dd52782bb0d521d3876c..ea747ac5da70558f4280ae64aa6982ca709071e8 100644 (file)
@@ -31,9 +31,6 @@ tk.mainloop()
 """
 
 import sys
-if sys.platform == "win32":
-    # Attempt to configure Tcl/Tk without requiring PATH
-    from tkinter import _fix
 
 import _tkinter # If this fails your Python may not be configured for Tk
 TclError = _tkinter.TclError
diff --git a/Lib/tkinter/_fix.py b/Lib/tkinter/_fix.py
deleted file mode 100644 (file)
index fa88734..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-import sys, os
-
-# Delay import _tkinter until we have set TCL_LIBRARY,
-# so that Tcl_FindExecutable has a chance to locate its
-# encoding directory.
-
-# Unfortunately, we cannot know the TCL_LIBRARY directory
-# if we don't know the tcl version, which we cannot find out
-# without import Tcl. Fortunately, Tcl will itself look in
-# <TCL_LIBRARY>\..\tcl<TCL_VERSION>, so anything close to
-# the real Tcl library will do.
-
-# Expand symbolic links on Vista
-try:
-    import ctypes
-    ctypes.windll.kernel32.GetFinalPathNameByHandleW
-except (ImportError, AttributeError):
-    def convert_path(s):
-        return s
-else:
-    def convert_path(s):
-        if isinstance(s, bytes):
-            s = s.decode("mbcs")
-        hdir = ctypes.windll.kernel32.\
-            CreateFileW(s, 0x80,    # FILE_READ_ATTRIBUTES
-                        1,          # FILE_SHARE_READ
-                        None, 3,    # OPEN_EXISTING
-                        0x02000000, # FILE_FLAG_BACKUP_SEMANTICS
-                        None)
-        if hdir == -1:
-            # Cannot open directory, give up
-            return s
-        buf = ctypes.create_unicode_buffer("", 32768)
-        res = ctypes.windll.kernel32.\
-            GetFinalPathNameByHandleW(hdir, buf, len(buf),
-                                      0) # VOLUME_NAME_DOS
-        ctypes.windll.kernel32.CloseHandle(hdir)
-        if res == 0:
-            # Conversion failed (e.g. network location)
-            return s
-        s = buf[:res]
-        # Ignore leading \\?\
-        if s.startswith("\\\\?\\"):
-            s = s[4:]
-        if s.startswith("UNC"):
-            s = "\\" + s[3:]
-        return s
-
-prefix = os.path.join(sys.base_prefix,"tcl")
-if not os.path.exists(prefix):
-    # devdir/externals/tcltk/lib
-    prefix = os.path.join(sys.base_prefix, "externals", "tcltk", "lib")
-    prefix = os.path.abspath(prefix)
-# if this does not exist, no further search is needed
-if os.path.exists(prefix):
-    prefix = convert_path(prefix)
-    if "TCL_LIBRARY" not in os.environ:
-        for name in os.listdir(prefix):
-            if name.startswith("tcl"):
-                tcldir = os.path.join(prefix,name)
-                if os.path.isdir(tcldir):
-                    os.environ["TCL_LIBRARY"] = tcldir
-    # Compute TK_LIBRARY, knowing that it has the same version
-    # as Tcl
-    import _tkinter
-    ver = str(_tkinter.TCL_VERSION)
-    if "TK_LIBRARY" not in os.environ:
-        v = os.path.join(prefix, 'tk'+ver)
-        if os.path.exists(os.path.join(v, "tclIndex")):
-            os.environ['TK_LIBRARY'] = v
-    # We don't know the Tix version, so we must search the entire
-    # directory
-    if "TIX_LIBRARY" not in os.environ:
-        for name in os.listdir(prefix):
-            if name.startswith("tix"):
-                tixdir = os.path.join(prefix,name)
-                if os.path.isdir(tixdir):
-                    os.environ["TIX_LIBRARY"] = tixdir
index 891b61ebf39492dc6f9cc9dbc35664223506b821..9c858dbaf1a7b7302d6071fee157a628ea8c4173 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -58,6 +58,10 @@ Core and Builtins
 Library
 -------
 
+- Issue #20035: Replaced the ``tkinter._fix`` module used for setting up the
+  Tcl/Tk environment on Windows with a private function in the ``_tkinter``
+  module that makes no permanent changes to the environment.
+
 - Issue #24257: Fixed segmentation fault in sqlite3.Row constructor with faked
   cursor type.
 
index 4da836e440e2b48338dc1f68081d827783eb4321..41ad5f91f7e6abff75e71fd687641915321ea2bb 100644 (file)
@@ -101,7 +101,65 @@ Copyright (C) 1994 Steen Lumholt.
 #ifdef MS_WINDOWS
 #include <conio.h>
 #define WAIT_FOR_STDIN
+
+static PyObject *
+_get_tcl_lib_path()
+{
+    static PyObject *tcl_library_path = NULL;
+    static int already_checked = 0;
+
+    if (already_checked == 0) {
+        PyObject *prefix;
+        struct stat stat_buf;
+        int stat_return_value;
+
+        prefix = PyUnicode_FromWideChar(Py_GetPrefix(), -1);
+        if (prefix == NULL) {
+            return NULL;
+        }
+
+        /* Check expected location for an installed Python first */
+        tcl_library_path = PyUnicode_FromString("\\tcl\\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 see if
+               we're a repository build */
+            errno = 0;
+#ifdef Py_TCLTK_DIR
+            tcl_library_path = PyUnicode_FromString(
+                                    Py_TCLTK_DIR "\\lib\\tcl" TCL_VERSION);
+            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) {
+                /* tcltkDir for a repository build doesn't exist either,
+                   reset errno and leave Tcl to its own devices */
+                errno = 0;
+                tcl_library_path = NULL;
+            }
+#else
+            tcl_library_path = NULL;
 #endif
+        }
+        already_checked = 1;
+    }
+    return tcl_library_path;
+}
+#endif /* MS_WINDOWS */
 
 #ifdef WITH_THREAD
 
@@ -681,6 +739,33 @@ Tkapp_New(const char *screenName, const char *className,
         PyMem_Free(args);
     }
 
+#ifdef MS_WINDOWS
+    {
+        PyObject *str_path;
+        PyObject *utf8_path;
+        DWORD ret;
+
+        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_AsString(utf8_path),
+                           TCL_GLOBAL_ONLY);
+                Py_DECREF(utf8_path);
+            }
+        }
+    }
+#endif
+
     if (Tcl_AppInit(v->interp) != TCL_OK) {
         PyObject *result = Tkinter_Error((PyObject *)v);
 #ifdef TKINTER_PROTECT_LOADTK
@@ -3517,8 +3602,40 @@ PyInit__tkinter(void)
     uexe = PyUnicode_FromWideChar(Py_GetProgramName(), -1);
     if (uexe) {
         cexe = PyUnicode_EncodeFSDefault(uexe);
-        if (cexe)
+        if (cexe) {
+#ifdef MS_WINDOWS
+            int set_var = 0;
+            PyObject *str_path;
+            wchar_t *wcs_path;
+            DWORD ret;
+
+            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) {
+                    wcs_path = PyUnicode_AsWideCharString(str_path, NULL);
+                    if (wcs_path == NULL) {
+                        return NULL;
+                    }
+                    SetEnvironmentVariableW(L"TCL_LIBRARY", wcs_path);
+                    set_var = 1;
+                }
+            }
+
             Tcl_FindExecutable(PyBytes_AsString(cexe));
+
+            if (set_var) {
+                SetEnvironmentVariableW(L"TCL_LIBRARY", NULL);
+                PyMem_Free(wcs_path);
+            }
+#else
+            Tcl_FindExecutable(PyBytes_AsString(cexe));
+#endif /* MS_WINDOWS */
+        }
         Py_XDECREF(cexe);
         Py_DECREF(uexe);
     }
index e1408a18ea3ea9fb83eadb117a5737d261293f13..f3185eb28b8cb511a4c11c165573e7c70280d4fd 100644 (file)
@@ -63,6 +63,7 @@
     <ClCompile>
       <AdditionalIncludeDirectories>$(tcltkDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>WITH_APPINIT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(BuildForRelease)' != 'true'">Py_TCLTK_DIR="$(tcltkDir.TrimEnd('\').Replace('\', '\\'))";%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
       <AdditionalDependencies>$(tcltkLib);%(AdditionalDependencies)</AdditionalDependencies>
index 044026fa779afb432761acf534d8ab068fed61ea..9467e86dcf8714798cbd00822e39d21cfe38f850 100644 (file)
@@ -1,7 +1,5 @@
 @rem Used by the buildbot "test" step.\r
 \r
 setlocal\r
-rem The following line should be removed before #20035 is closed\r
-set TCL_LIBRARY=%~dp0..\..\externals\tcltk64\lib\tcl8.6\r
 \r
 call "%~dp0..\..\PCbuild\rt.bat" -d -q -x64 -uall -rwW -n --timeout=3600 %*\r
index 427957b78911f5246289cdafb14f63b63486aaa5..995f0122353b9b8521903b7179e664f2a291be32 100644 (file)
@@ -1,7 +1,5 @@
 @rem Used by the buildbot "test" step.\r
 \r
 setlocal\r
-rem The following line should be removed before #20035 is closed\r
-set TCL_LIBRARY=%~dp0..\..\externals\tcltk\lib\tcl8.6\r
 \r
 call "%~dp0..\..\PCbuild\rt.bat" -d -q -uall -rwW -n --timeout=3600 %*\r