Issue #20035: Reimplement tkinter._fix module as a C function.

The new private C function makes no permanent changes to the environment
and is #ifdef'd out on non-Windows platforms.
This commit is contained in:
Zachary Ware 2015-05-22 11:36:53 -05:00
parent 3cfec2e2fc
commit 7dc9dea778
12 changed files with 134 additions and 99 deletions

View File

@ -1,4 +1,4 @@
**************************** ****************************
What's New In Python 3.5 What's New In Python 3.5
**************************** ****************************
@ -659,6 +659,14 @@ time
* The :func:`time.monotonic` function is now always available. (Contributed by * The :func:`time.monotonic` function is now always available. (Contributed by
Victor Stinner in :issue:`22043`.) 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 types
----- -----

View File

@ -7,9 +7,7 @@ from test import support
# Skip this test if the _tkinter module wasn't built. # Skip this test if the _tkinter module wasn't built.
_tkinter = support.import_module('_tkinter') _tkinter = support.import_module('_tkinter')
# Make sure tkinter._fix runs to set up the environment import tkinter
tkinter = support.import_fresh_module('tkinter')
from tkinter import Tcl from tkinter import Tcl
from _tkinter import TclError from _tkinter import TclError

View File

@ -2,9 +2,6 @@ from test import support
# Skip test if _tkinter wasn't built. # Skip test if _tkinter wasn't built.
support.import_module('_tkinter') 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. # Skip test if tk cannot be initialized.
support.requires('gui') support.requires('gui')

View File

@ -5,12 +5,10 @@ from test import support
# Skip this test if _tkinter wasn't built. # Skip this test if _tkinter wasn't built.
support.import_module('_tkinter') 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. # Skip test if tk cannot be initialized.
support.requires('gui') support.requires('gui')
import tkinter
from _tkinter import TclError from _tkinter import TclError
from tkinter import ttk from tkinter import ttk
from tkinter.test import runtktests from tkinter.test import runtktests

View File

@ -4,9 +4,6 @@ from test import support
# Skip this test if _tkinter does not exist. # Skip this test if _tkinter does not exist.
support.import_module('_tkinter') support.import_module('_tkinter')
# Make sure tkinter._fix runs to set up the environment
support.import_fresh_module('tkinter')
from tkinter.test import runtktests from tkinter.test import runtktests
def test_main(): def test_main():

View File

@ -31,9 +31,6 @@ tk.mainloop()
""" """
import sys 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 import _tkinter # If this fails your Python may not be configured for Tk
TclError = _tkinter.TclError TclError = _tkinter.TclError

View File

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

View File

@ -58,6 +58,10 @@ Core and Builtins
Library 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 - Issue #24257: Fixed segmentation fault in sqlite3.Row constructor with faked
cursor type. cursor type.

View File

@ -101,7 +101,65 @@ Copyright (C) 1994 Steen Lumholt.
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
#include <conio.h> #include <conio.h>
#define WAIT_FOR_STDIN #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 #endif
}
already_checked = 1;
}
return tcl_library_path;
}
#endif /* MS_WINDOWS */
#ifdef WITH_THREAD #ifdef WITH_THREAD
@ -681,6 +739,33 @@ Tkapp_New(const char *screenName, const char *className,
PyMem_Free(args); 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) { if (Tcl_AppInit(v->interp) != TCL_OK) {
PyObject *result = Tkinter_Error((PyObject *)v); PyObject *result = Tkinter_Error((PyObject *)v);
#ifdef TKINTER_PROTECT_LOADTK #ifdef TKINTER_PROTECT_LOADTK
@ -3517,8 +3602,40 @@ PyInit__tkinter(void)
uexe = PyUnicode_FromWideChar(Py_GetProgramName(), -1); uexe = PyUnicode_FromWideChar(Py_GetProgramName(), -1);
if (uexe) { if (uexe) {
cexe = PyUnicode_EncodeFSDefault(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)); 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_XDECREF(cexe);
Py_DECREF(uexe); Py_DECREF(uexe);
} }

View File

@ -63,6 +63,7 @@
<ClCompile> <ClCompile>
<AdditionalIncludeDirectories>$(tcltkDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(tcltkDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WITH_APPINIT;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WITH_APPINIT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(BuildForRelease)' != 'true'">Py_TCLTK_DIR="$(tcltkDir.TrimEnd('\').Replace('\', '\\'))";%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile> </ClCompile>
<Link> <Link>
<AdditionalDependencies>$(tcltkLib);%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>$(tcltkLib);%(AdditionalDependencies)</AdditionalDependencies>

View File

@ -1,7 +1,5 @@
@rem Used by the buildbot "test" step. @rem Used by the buildbot "test" step.
setlocal 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 %* call "%~dp0..\..\PCbuild\rt.bat" -d -q -x64 -uall -rwW -n --timeout=3600 %*

View File

@ -1,7 +1,5 @@
@rem Used by the buildbot "test" step. @rem Used by the buildbot "test" step.
setlocal 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 %* call "%~dp0..\..\PCbuild\rt.bat" -d -q -uall -rwW -n --timeout=3600 %*