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