summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/whatsnew/3.5.rst10
-rw-r--r--Lib/test/test_tcl.py4
-rw-r--r--Lib/test/test_tk.py3
-rw-r--r--Lib/test/test_ttk_guionly.py4
-rw-r--r--Lib/test/test_ttk_textonly.py3
-rw-r--r--Lib/tkinter/__init__.py3
-rw-r--r--Lib/tkinter/_fix.py78
-rw-r--r--Misc/NEWS4
-rw-r--r--Modules/_tkinter.c119
-rw-r--r--PCbuild/_tkinter.vcxproj1
-rw-r--r--Tools/buildbot/test-amd64.bat2
-rw-r--r--Tools/buildbot/test.bat2
12 files changed, 134 insertions, 99 deletions
diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
index b2571ae..a149417 100644
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -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
-----
diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py
index b612517..5be645a 100644
--- a/Lib/test/test_tcl.py
+++ b/Lib/test/test_tcl.py
@@ -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
diff --git a/Lib/test/test_tk.py b/Lib/test/test_tk.py
index 62729f0..48cefd9 100644
--- a/Lib/test/test_tk.py
+++ b/Lib/test/test_tk.py
@@ -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')
diff --git a/Lib/test/test_ttk_guionly.py b/Lib/test/test_ttk_guionly.py
index fcdedac..490e723 100644
--- a/Lib/test/test_ttk_guionly.py
+++ b/Lib/test/test_ttk_guionly.py
@@ -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
diff --git a/Lib/test/test_ttk_textonly.py b/Lib/test/test_ttk_textonly.py
index 1cfeb15..566fc9d 100644
--- a/Lib/test/test_ttk_textonly.py
+++ b/Lib/test/test_ttk_textonly.py
@@ -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():
diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py
index 196809b..ea747ac 100644
--- a/Lib/tkinter/__init__.py
+++ b/Lib/tkinter/__init__.py
@@ -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
index fa88734..0000000
--- a/Lib/tkinter/_fix.py
+++ /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
diff --git a/Misc/NEWS b/Misc/NEWS
index 891b61e..9c858db 100644
--- 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.
diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c
index 4da836e..41ad5f9 100644
--- a/Modules/_tkinter.c
+++ b/Modules/_tkinter.c
@@ -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);
}
diff --git a/PCbuild/_tkinter.vcxproj b/PCbuild/_tkinter.vcxproj
index e1408a1..f3185eb 100644
--- a/PCbuild/_tkinter.vcxproj
+++ b/PCbuild/_tkinter.vcxproj
@@ -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>
diff --git a/Tools/buildbot/test-amd64.bat b/Tools/buildbot/test-amd64.bat
index 044026f..9467e86 100644
--- a/Tools/buildbot/test-amd64.bat
+++ b/Tools/buildbot/test-amd64.bat
@@ -1,7 +1,5 @@
@rem Used by the buildbot "test" step.
setlocal
-rem The following line should be removed before #20035 is closed
-set TCL_LIBRARY=%~dp0..\..\externals\tcltk64\lib\tcl8.6
call "%~dp0..\..\PCbuild\rt.bat" -d -q -x64 -uall -rwW -n --timeout=3600 %*
diff --git a/Tools/buildbot/test.bat b/Tools/buildbot/test.bat
index 427957b..995f012 100644
--- a/Tools/buildbot/test.bat
+++ b/Tools/buildbot/test.bat
@@ -1,7 +1,5 @@
@rem Used by the buildbot "test" step.
setlocal
-rem The following line should be removed before #20035 is closed
-set TCL_LIBRARY=%~dp0..\..\externals\tcltk\lib\tcl8.6
call "%~dp0..\..\PCbuild\rt.bat" -d -q -uall -rwW -n --timeout=3600 %*