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:
parent
3cfec2e2fc
commit
7dc9dea778
|
@ -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
|
||||
-----
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %*
|
||||
|
|
|
@ -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 %*
|
||||
|
|
Loading…
Reference in New Issue