bpo-13153: Use OS native encoding for converting between Python and Tcl. (GH-16545)

On Windows use UTF-16 (or UTF-32 for 32-bit Tcl_UniChar) with the
"surrogatepass" error handler for converting to/from Tcl Unicode objects.

On Linux use UTF-8 with the "surrogateescape" error handler for converting
to/from Tcl String objects.

Converting strings from Tcl to Python and back now never fails
(except MemoryError).
This commit is contained in:
Serhiy Storchaka 2019-10-04 13:09:52 +03:00 committed by GitHub
parent 2290b23dfc
commit 06cb94bc84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 241 additions and 242 deletions

View File

@ -358,21 +358,6 @@ class EditorWindow(object):
Font(text, font=text.cget('font')).measure('0') Font(text, font=text.cget('font')).measure('0')
self.width = pixel_width // zero_char_width self.width = pixel_width // zero_char_width
def _filename_to_unicode(self, filename):
"""Return filename as BMP unicode so displayable in Tk."""
# Decode bytes to unicode.
if isinstance(filename, bytes):
try:
filename = filename.decode(self.filesystemencoding)
except UnicodeDecodeError:
try:
filename = filename.decode(self.encoding)
except UnicodeDecodeError:
# byte-to-byte conversion
filename = filename.decode('iso8859-1')
# Replace non-BMP char with diamond questionmark.
return re.sub('[\U00010000-\U0010FFFF]', '\ufffd', filename)
def new_callback(self, event): def new_callback(self, event):
dirname, basename = self.io.defaultfilename() dirname, basename = self.io.defaultfilename()
self.flist.new(dirname) self.flist.new(dirname)
@ -963,10 +948,8 @@ class EditorWindow(object):
menu.delete(0, END) # clear, and rebuild: menu.delete(0, END) # clear, and rebuild:
for i, file_name in enumerate(rf_list): for i, file_name in enumerate(rf_list):
file_name = file_name.rstrip() # zap \n file_name = file_name.rstrip() # zap \n
# make unicode string to display non-ASCII chars correctly
ufile_name = self._filename_to_unicode(file_name)
callback = instance.__recent_file_callback(file_name) callback = instance.__recent_file_callback(file_name)
menu.add_command(label=ulchars[i] + " " + ufile_name, menu.add_command(label=ulchars[i] + " " + file_name,
command=callback, command=callback,
underline=0) underline=0)
@ -1004,16 +987,10 @@ class EditorWindow(object):
def short_title(self): def short_title(self):
filename = self.io.filename filename = self.io.filename
if filename: return os.path.basename(filename) if filename else "untitled"
filename = os.path.basename(filename)
else:
filename = "untitled"
# return unicode string to display non-ASCII chars correctly
return self._filename_to_unicode(filename)
def long_title(self): def long_title(self):
# return unicode string to display non-ASCII chars correctly return self.io.filename or ""
return self._filename_to_unicode(self.io.filename or "")
def center_insert_event(self, event): def center_insert_event(self, event):
self.center() self.center()

View File

@ -30,18 +30,6 @@ class EditorWindowTest(unittest.TestCase):
e._close() e._close()
class EditorFunctionTest(unittest.TestCase):
def test_filename_to_unicode(self):
func = Editor._filename_to_unicode
class dummy():
filesystemencoding = 'utf-8'
pairs = (('abc', 'abc'), ('a\U00011111c', 'a\ufffdc'),
(b'abc', 'abc'), (b'a\xf0\x91\x84\x91c', 'a\ufffdc'))
for inp, out in pairs:
self.assertEqual(func(dummy, inp), out)
class TestGetLineIndent(unittest.TestCase): class TestGetLineIndent(unittest.TestCase):
def test_empty_lines(self): def test_empty_lines(self):
for tabwidth in [1, 2, 4, 6, 8]: for tabwidth in [1, 2, 4, 6, 8]:

View File

@ -679,14 +679,6 @@ class ModifiedInterpreter(InteractiveInterpreter):
self.more = 0 self.more = 0
# at the moment, InteractiveInterpreter expects str # at the moment, InteractiveInterpreter expects str
assert isinstance(source, str) assert isinstance(source, str)
#if isinstance(source, str):
# from idlelib import iomenu
# try:
# source = source.encode(iomenu.encoding)
# except UnicodeError:
# self.tkconsole.resetoutput()
# self.write("Unsupported characters in input\n")
# return
# InteractiveInterpreter.runsource() calls its runcode() method, # InteractiveInterpreter.runsource() calls its runcode() method,
# which is overridden (see below) # which is overridden (see below)
return InteractiveInterpreter.runsource(self, source, filename) return InteractiveInterpreter.runsource(self, source, filename)
@ -1298,16 +1290,6 @@ class PyShell(OutputWindow):
self.set_line_and_column() self.set_line_and_column()
def write(self, s, tags=()): def write(self, s, tags=()):
if isinstance(s, str) and len(s) and max(s) > '\uffff':
# Tk doesn't support outputting non-BMP characters
# Let's assume what printed string is not very long,
# find first non-BMP character and construct informative
# UnicodeEncodeError exception.
for start, char in enumerate(s):
if char > '\uffff':
break
raise UnicodeEncodeError("UCS-2", char, start, start+1,
'Non-BMP character not supported in Tk')
try: try:
self.text.mark_gravity("iomark", "right") self.text.mark_gravity("iomark", "right")
count = OutputWindow.write(self, s, tags, "iomark") count = OutputWindow.write(self, s, tags, "iomark")

View File

@ -147,8 +147,7 @@ class ScriptBinding:
interp = self.shell.interp interp = self.shell.interp
if pyshell.use_subprocess and restart: if pyshell.use_subprocess and restart:
interp.restart_subprocess( interp.restart_subprocess(
with_cwd=False, filename= with_cwd=False, filename=filename)
self.editwin._filename_to_unicode(filename))
dirname = os.path.dirname(filename) dirname = os.path.dirname(filename)
argv = [filename] argv = [filename]
if self.cli_args: if self.cli_args:

View File

@ -429,9 +429,12 @@ class TclTest(unittest.TestCase):
self.assertEqual(passValue(False), False if self.wantobjects else '0') self.assertEqual(passValue(False), False if self.wantobjects else '0')
self.assertEqual(passValue('string'), 'string') self.assertEqual(passValue('string'), 'string')
self.assertEqual(passValue('string\u20ac'), 'string\u20ac') self.assertEqual(passValue('string\u20ac'), 'string\u20ac')
self.assertEqual(passValue('string\U0001f4bb'), 'string\U0001f4bb')
self.assertEqual(passValue('str\x00ing'), 'str\x00ing') self.assertEqual(passValue('str\x00ing'), 'str\x00ing')
self.assertEqual(passValue('str\x00ing\xbd'), 'str\x00ing\xbd') self.assertEqual(passValue('str\x00ing\xbd'), 'str\x00ing\xbd')
self.assertEqual(passValue('str\x00ing\u20ac'), 'str\x00ing\u20ac') self.assertEqual(passValue('str\x00ing\u20ac'), 'str\x00ing\u20ac')
self.assertEqual(passValue('str\x00ing\U0001f4bb'),
'str\x00ing\U0001f4bb')
self.assertEqual(passValue(b'str\x00ing'), self.assertEqual(passValue(b'str\x00ing'),
b'str\x00ing' if self.wantobjects else 'str\x00ing') b'str\x00ing' if self.wantobjects else 'str\x00ing')
self.assertEqual(passValue(b'str\xc0\x80ing'), self.assertEqual(passValue(b'str\xc0\x80ing'),
@ -490,6 +493,7 @@ class TclTest(unittest.TestCase):
check('string') check('string')
check('string\xbd') check('string\xbd')
check('string\u20ac') check('string\u20ac')
check('string\U0001f4bb')
check('') check('')
check(b'string', 'string') check(b'string', 'string')
check(b'string\xe2\x82\xac', 'string\xe2\x82\xac') check(b'string\xe2\x82\xac', 'string\xe2\x82\xac')
@ -531,6 +535,7 @@ class TclTest(unittest.TestCase):
('a\n b\t\r c\n ', ('a', 'b', 'c')), ('a\n b\t\r c\n ', ('a', 'b', 'c')),
(b'a\n b\t\r c\n ', ('a', 'b', 'c')), (b'a\n b\t\r c\n ', ('a', 'b', 'c')),
('a \u20ac', ('a', '\u20ac')), ('a \u20ac', ('a', '\u20ac')),
('a \U0001f4bb', ('a', '\U0001f4bb')),
(b'a \xe2\x82\xac', ('a', '\u20ac')), (b'a \xe2\x82\xac', ('a', '\u20ac')),
(b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')), (b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')),
('a {b c}', ('a', 'b c')), ('a {b c}', ('a', 'b c')),

View File

@ -170,6 +170,28 @@ class MiscTest(AbstractTkTest, unittest.TestCase):
with self.assertRaises(tkinter.TclError): with self.assertRaises(tkinter.TclError):
root.tk.call('after', 'info', idle1) root.tk.call('after', 'info', idle1)
def test_clipboard(self):
root = self.root
root.clipboard_clear()
root.clipboard_append('Ùñî')
self.assertEqual(root.clipboard_get(), 'Ùñî')
root.clipboard_append('çōđě')
self.assertEqual(root.clipboard_get(), 'Ùñîçōđě')
root.clipboard_clear()
with self.assertRaises(tkinter.TclError):
root.clipboard_get()
def test_clipboard_astral(self):
root = self.root
root.clipboard_clear()
root.clipboard_append('𝔘𝔫𝔦')
self.assertEqual(root.clipboard_get(), '𝔘𝔫𝔦')
root.clipboard_append('𝔠𝔬𝔡𝔢')
self.assertEqual(root.clipboard_get(), '𝔘𝔫𝔦𝔠𝔬𝔡𝔢')
root.clipboard_clear()
with self.assertRaises(tkinter.TclError):
root.clipboard_get()
tests_gui = (MiscTest, ) tests_gui = (MiscTest, )

View File

@ -489,8 +489,7 @@ class ComboboxTest(EntryTest, unittest.TestCase):
expected=('mon', 'tue', 'wed', 'thur')) expected=('mon', 'tue', 'wed', 'thur'))
self.checkParam(self.combo, 'values', ('mon', 'tue', 'wed', 'thur')) self.checkParam(self.combo, 'values', ('mon', 'tue', 'wed', 'thur'))
self.checkParam(self.combo, 'values', (42, 3.14, '', 'any string')) self.checkParam(self.combo, 'values', (42, 3.14, '', 'any string'))
self.checkParam(self.combo, 'values', '', self.checkParam(self.combo, 'values', '')
expected='' if get_tk_patchlevel() < (8, 5, 10) else ())
self.combo['values'] = ['a', 1, 'c'] self.combo['values'] = ['a', 1, 'c']
@ -1245,12 +1244,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
expected=('mon', 'tue', 'wed', 'thur')) expected=('mon', 'tue', 'wed', 'thur'))
self.checkParam(self.spin, 'values', ('mon', 'tue', 'wed', 'thur')) self.checkParam(self.spin, 'values', ('mon', 'tue', 'wed', 'thur'))
self.checkParam(self.spin, 'values', (42, 3.14, '', 'any string')) self.checkParam(self.spin, 'values', (42, 3.14, '', 'any string'))
self.checkParam( self.checkParam(self.spin, 'values', '')
self.spin,
'values',
'',
expected='' if get_tk_patchlevel() < (8, 5, 10) else ()
)
self.spin['values'] = ['a', 1, 'c'] self.spin['values'] = ['a', 1, 'c']
@ -1308,8 +1302,7 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase):
self.checkParam(widget, 'columns', 'a b c', self.checkParam(widget, 'columns', 'a b c',
expected=('a', 'b', 'c')) expected=('a', 'b', 'c'))
self.checkParam(widget, 'columns', ('a', 'b', 'c')) self.checkParam(widget, 'columns', ('a', 'b', 'c'))
self.checkParam(widget, 'columns', (), self.checkParam(widget, 'columns', '')
expected='' if get_tk_patchlevel() < (8, 5, 10) else ())
def test_displaycolumns(self): def test_displaycolumns(self):
widget = self.create() widget = self.create()

View File

@ -0,0 +1,4 @@
OS native encoding is now used for converting between Python strings and
Tcl objects. This allows to display, copy and paste to clipboard emoji and
other non-BMP characters. Converting strings from Tcl to Python and back
now never fails (except MemoryError).

View File

@ -96,6 +96,24 @@ Copyright (C) 1994 Steen Lumholt.
#endif /* HAVE_CREATEFILEHANDLER */ #endif /* HAVE_CREATEFILEHANDLER */
/* Use OS native encoding for converting between Python strings and
Tcl objects.
On Windows use UTF-16 (or UTF-32 for 32-bit Tcl_UniChar) with the
"surrogatepass" error handler for converting to/from Tcl Unicode objects.
On Linux use UTF-8 with the "surrogateescape" error handler for converting
to/from Tcl String objects. */
#ifdef MS_WINDOWS
#define USE_TCL_UNICODE 1
#else
#define USE_TCL_UNICODE 0
#endif
#if PY_LITTLE_ENDIAN
#define NATIVE_BYTEORDER -1
#else
#define NATIVE_BYTEORDER 1
#endif
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
#include <conio.h> #include <conio.h>
#define WAIT_FOR_STDIN #define WAIT_FOR_STDIN
@ -290,7 +308,6 @@ typedef struct {
} TkappObject; } TkappObject;
#define Tkapp_Interp(v) (((TkappObject *) (v))->interp) #define Tkapp_Interp(v) (((TkappObject *) (v))->interp)
#define Tkapp_Result(v) Tcl_GetStringResult(Tkapp_Interp(v))
#define DEBUG_REFCNT(v) (printf("DEBUG: id=%p, refcnt=%i\n", \ #define DEBUG_REFCNT(v) (printf("DEBUG: id=%p, refcnt=%i\n", \
(void *) v, Py_REFCNT(v))) (void *) v, Py_REFCNT(v)))
@ -311,10 +328,16 @@ static int tk_load_failed = 0;
#endif #endif
static PyObject *Tkapp_UnicodeResult(TkappObject *);
static PyObject * static PyObject *
Tkinter_Error(PyObject *v) Tkinter_Error(TkappObject *self)
{ {
PyErr_SetString(Tkinter_TclError, Tkapp_Result(v)); PyObject *res = Tkapp_UnicodeResult(self);
if (res != NULL) {
PyErr_SetObject(Tkinter_TclError, res);
Py_DECREF(res);
}
return NULL; return NULL;
} }
@ -368,30 +391,35 @@ static PyObject *
unicodeFromTclStringAndSize(const char *s, Py_ssize_t size) unicodeFromTclStringAndSize(const char *s, Py_ssize_t size)
{ {
PyObject *r = PyUnicode_DecodeUTF8(s, size, NULL); PyObject *r = PyUnicode_DecodeUTF8(s, size, NULL);
if (!r && PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) { if (r != NULL || !PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) {
/* Tcl encodes null character as \xc0\x80 */ return r;
if (memchr(s, '\xc0', size)) { }
char *buf, *q;
const char *e = s + size; char *buf = NULL;
PyErr_Clear(); PyErr_Clear();
q = buf = (char *)PyMem_Malloc(size); /* Tcl encodes null character as \xc0\x80 */
if (buf == NULL) { if (memchr(s, '\xc0', size)) {
PyErr_NoMemory(); char *q;
return NULL; const char *e = s + size;
} q = buf = (char *)PyMem_Malloc(size);
while (s != e) { if (buf == NULL) {
if (s + 1 != e && s[0] == '\xc0' && s[1] == '\x80') { PyErr_NoMemory();
*q++ = '\0'; return NULL;
s += 2;
}
else
*q++ = *s++;
}
s = buf;
size = q - s;
r = PyUnicode_DecodeUTF8(s, size, NULL);
PyMem_Free(buf);
} }
while (s != e) {
if (s + 1 != e && s[0] == '\xc0' && s[1] == '\x80') {
*q++ = '\0';
s += 2;
}
else
*q++ = *s++;
}
s = buf;
size = q - s;
}
r = PyUnicode_DecodeUTF8(s, size, "surrogateescape");
if (buf != NULL) {
PyMem_Free(buf);
} }
return r; return r;
} }
@ -406,8 +434,21 @@ static PyObject *
unicodeFromTclObj(Tcl_Obj *value) unicodeFromTclObj(Tcl_Obj *value)
{ {
int len; int len;
char *s = Tcl_GetStringFromObj(value, &len); #if USE_TCL_UNICODE
int byteorder = NATIVE_BYTEORDER;
const Tcl_UniChar *u = Tcl_GetUnicodeFromObj(value, &len);
if (sizeof(Tcl_UniChar) == 2)
return PyUnicode_DecodeUTF16((const char *)u, len * 2,
"surrogatepass", &byteorder);
else if (sizeof(Tcl_UniChar) == 4)
return PyUnicode_DecodeUTF32((const char *)u, len * 4,
"surrogatepass", &byteorder);
else
Py_UNREACHABLE();
#else
const char *s = Tcl_GetStringFromObj(value, &len);
return unicodeFromTclStringAndSize(s, len); return unicodeFromTclStringAndSize(s, len);
#endif
} }
@ -746,7 +787,7 @@ Tkapp_New(const char *screenName, const char *className,
#endif #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(v);
#ifdef TKINTER_PROTECT_LOADTK #ifdef TKINTER_PROTECT_LOADTK
if (wantTk) { if (wantTk) {
const char *_tkinter_tk_failed; const char *_tkinter_tk_failed;
@ -817,12 +858,6 @@ PyTclObject_dealloc(PyTclObject *self)
Py_DECREF(tp); Py_DECREF(tp);
} }
static const char *
PyTclObject_TclString(PyObject *self)
{
return Tcl_GetString(((PyTclObject*)self)->value);
}
/* Like _str, but create Unicode if necessary. */ /* Like _str, but create Unicode if necessary. */
PyDoc_STRVAR(PyTclObject_string__doc__, PyDoc_STRVAR(PyTclObject_string__doc__,
"the string representation of this object, either as str or bytes"); "the string representation of this object, either as str or bytes");
@ -1048,53 +1083,51 @@ AsObj(PyObject *value)
} }
if (PyUnicode_Check(value)) { if (PyUnicode_Check(value)) {
void *inbuf;
Py_ssize_t size;
int kind;
Tcl_UniChar *outbuf = NULL;
Py_ssize_t i;
size_t allocsize;
if (PyUnicode_READY(value) == -1) if (PyUnicode_READY(value) == -1)
return NULL; return NULL;
inbuf = PyUnicode_DATA(value); Py_ssize_t size = PyUnicode_GET_LENGTH(value);
size = PyUnicode_GET_LENGTH(value); if (size == 0) {
if (size == 0) return Tcl_NewStringObj("", 0);
return Tcl_NewUnicodeObj((const void *)"", 0); }
if (!CHECK_SIZE(size, sizeof(Tcl_UniChar))) { if (!CHECK_SIZE(size, sizeof(Tcl_UniChar))) {
PyErr_SetString(PyExc_OverflowError, "string is too long"); PyErr_SetString(PyExc_OverflowError, "string is too long");
return NULL; return NULL;
} }
kind = PyUnicode_KIND(value); if (PyUnicode_IS_ASCII(value)) {
if (kind == sizeof(Tcl_UniChar)) return Tcl_NewStringObj((const char *)PyUnicode_DATA(value),
return Tcl_NewUnicodeObj(inbuf, (int)size); (int)size);
allocsize = ((size_t)size) * sizeof(Tcl_UniChar); }
outbuf = (Tcl_UniChar*)PyMem_Malloc(allocsize);
/* Else overflow occurred, and we take the next exit */ PyObject *encoded;
if (!outbuf) { #if USE_TCL_UNICODE
PyErr_NoMemory(); if (sizeof(Tcl_UniChar) == 2)
encoded = _PyUnicode_EncodeUTF16(value,
"surrogatepass", NATIVE_BYTEORDER);
else if (sizeof(Tcl_UniChar) == 4)
encoded = _PyUnicode_EncodeUTF32(value,
"surrogatepass", NATIVE_BYTEORDER);
else
Py_UNREACHABLE();
#else
encoded = _PyUnicode_AsUTF8String(value, "surrogateescape");
#endif
if (!encoded) {
return NULL; return NULL;
} }
for (i = 0; i < size; i++) { size = PyBytes_GET_SIZE(encoded);
Py_UCS4 ch = PyUnicode_READ(kind, inbuf, i); if (size > INT_MAX) {
/* We cannot test for sizeof(Tcl_UniChar) directly, Py_DECREF(encoded);
so we test for UTF-8 size instead. */ PyErr_SetString(PyExc_OverflowError, "string is too long");
#if TCL_UTF_MAX == 3 return NULL;
if (ch >= 0x10000) {
/* Tcl doesn't do UTF-16, yet. */
PyErr_Format(Tkinter_TclError,
"character U+%x is above the range "
"(U+0000-U+FFFF) allowed by Tcl",
ch);
PyMem_Free(outbuf);
return NULL;
}
#endif
outbuf[i] = ch;
} }
result = Tcl_NewUnicodeObj(outbuf, (int)size); #if USE_TCL_UNICODE
PyMem_Free(outbuf); result = Tcl_NewUnicodeObj((const Tcl_UniChar *)PyBytes_AS_STRING(encoded),
(int)(size / sizeof(Tcl_UniChar)));
#else
result = Tcl_NewStringObj(PyBytes_AS_STRING(encoded), (int)size);
#endif
Py_DECREF(encoded);
return result; return result;
} }
@ -1113,7 +1146,7 @@ AsObj(PyObject *value)
} }
static PyObject * static PyObject *
fromBoolean(PyObject* tkapp, Tcl_Obj *value) fromBoolean(TkappObject *tkapp, Tcl_Obj *value)
{ {
int boolValue; int boolValue;
if (Tcl_GetBooleanFromObj(Tkapp_Interp(tkapp), value, &boolValue) == TCL_ERROR) if (Tcl_GetBooleanFromObj(Tkapp_Interp(tkapp), value, &boolValue) == TCL_ERROR)
@ -1122,7 +1155,7 @@ fromBoolean(PyObject* tkapp, Tcl_Obj *value)
} }
static PyObject* static PyObject*
fromWideIntObj(PyObject* tkapp, Tcl_Obj *value) fromWideIntObj(TkappObject *tkapp, Tcl_Obj *value)
{ {
Tcl_WideInt wideValue; Tcl_WideInt wideValue;
if (Tcl_GetWideIntFromObj(Tkapp_Interp(tkapp), value, &wideValue) == TCL_OK) { if (Tcl_GetWideIntFromObj(Tkapp_Interp(tkapp), value, &wideValue) == TCL_OK) {
@ -1138,7 +1171,7 @@ fromWideIntObj(PyObject* tkapp, Tcl_Obj *value)
#ifdef HAVE_LIBTOMMAMTH #ifdef HAVE_LIBTOMMAMTH
static PyObject* static PyObject*
fromBignumObj(PyObject* tkapp, Tcl_Obj *value) fromBignumObj(TkappObject *tkapp, Tcl_Obj *value)
{ {
mp_int bigValue; mp_int bigValue;
unsigned long numBytes; unsigned long numBytes;
@ -1174,32 +1207,31 @@ fromBignumObj(PyObject* tkapp, Tcl_Obj *value)
#endif #endif
static PyObject* static PyObject*
FromObj(PyObject* tkapp, Tcl_Obj *value) FromObj(TkappObject *tkapp, Tcl_Obj *value)
{ {
PyObject *result = NULL; PyObject *result = NULL;
TkappObject *app = (TkappObject*)tkapp;
Tcl_Interp *interp = Tkapp_Interp(tkapp); Tcl_Interp *interp = Tkapp_Interp(tkapp);
if (value->typePtr == NULL) { if (value->typePtr == NULL) {
return unicodeFromTclStringAndSize(value->bytes, value->length); return unicodeFromTclObj(value);
} }
if (value->typePtr == app->BooleanType || if (value->typePtr == tkapp->BooleanType ||
value->typePtr == app->OldBooleanType) { value->typePtr == tkapp->OldBooleanType) {
return fromBoolean(tkapp, value); return fromBoolean(tkapp, value);
} }
if (value->typePtr == app->ByteArrayType) { if (value->typePtr == tkapp->ByteArrayType) {
int size; int size;
char *data = (char*)Tcl_GetByteArrayFromObj(value, &size); char *data = (char*)Tcl_GetByteArrayFromObj(value, &size);
return PyBytes_FromStringAndSize(data, size); return PyBytes_FromStringAndSize(data, size);
} }
if (value->typePtr == app->DoubleType) { if (value->typePtr == tkapp->DoubleType) {
return PyFloat_FromDouble(value->internalRep.doubleValue); return PyFloat_FromDouble(value->internalRep.doubleValue);
} }
if (value->typePtr == app->IntType) { if (value->typePtr == tkapp->IntType) {
long longValue; long longValue;
if (Tcl_GetLongFromObj(interp, value, &longValue) == TCL_OK) if (Tcl_GetLongFromObj(interp, value, &longValue) == TCL_OK)
return PyLong_FromLong(longValue); return PyLong_FromLong(longValue);
@ -1207,8 +1239,8 @@ FromObj(PyObject* tkapp, Tcl_Obj *value)
fall through to wideInt handling. */ fall through to wideInt handling. */
} }
if (value->typePtr == app->IntType || if (value->typePtr == tkapp->IntType ||
value->typePtr == app->WideIntType) { value->typePtr == tkapp->WideIntType) {
result = fromWideIntObj(tkapp, value); result = fromWideIntObj(tkapp, value);
if (result != NULL || PyErr_Occurred()) if (result != NULL || PyErr_Occurred())
return result; return result;
@ -1218,14 +1250,14 @@ FromObj(PyObject* tkapp, Tcl_Obj *value)
} }
#ifdef HAVE_LIBTOMMAMTH #ifdef HAVE_LIBTOMMAMTH
if (value->typePtr == app->IntType || if (value->typePtr == tkapp->IntType ||
value->typePtr == app->WideIntType || value->typePtr == tkapp->WideIntType ||
value->typePtr == app->BignumType) { value->typePtr == tkapp->BignumType) {
return fromBignumObj(tkapp, value); return fromBignumObj(tkapp, value);
} }
#endif #endif
if (value->typePtr == app->ListType) { if (value->typePtr == tkapp->ListType) {
int size; int size;
int i, status; int i, status;
PyObject *elem; PyObject *elem;
@ -1253,30 +1285,28 @@ FromObj(PyObject* tkapp, Tcl_Obj *value)
return result; return result;
} }
if (value->typePtr == app->ProcBodyType) { if (value->typePtr == tkapp->ProcBodyType) {
/* fall through: return tcl object. */ /* fall through: return tcl object. */
} }
if (value->typePtr == app->StringType) { if (value->typePtr == tkapp->StringType) {
return PyUnicode_FromKindAndData( return unicodeFromTclObj(value);
sizeof(Tcl_UniChar), Tcl_GetUnicode(value),
Tcl_GetCharLength(value));
} }
#if TK_HEX_VERSION >= 0x08050000 #if TK_HEX_VERSION >= 0x08050000
if (app->BooleanType == NULL && if (tkapp->BooleanType == NULL &&
strcmp(value->typePtr->name, "booleanString") == 0) { strcmp(value->typePtr->name, "booleanString") == 0) {
/* booleanString type is not registered in Tcl */ /* booleanString type is not registered in Tcl */
app->BooleanType = value->typePtr; tkapp->BooleanType = value->typePtr;
return fromBoolean(tkapp, value); return fromBoolean(tkapp, value);
} }
#endif #endif
#ifdef HAVE_LIBTOMMAMTH #ifdef HAVE_LIBTOMMAMTH
if (app->BignumType == NULL && if (tkapp->BignumType == NULL &&
strcmp(value->typePtr->name, "bignum") == 0) { strcmp(value->typePtr->name, "bignum") == 0) {
/* bignum type is not registered in Tcl */ /* bignum type is not registered in Tcl */
app->BignumType = value->typePtr; tkapp->BignumType = value->typePtr;
return fromBignumObj(tkapp, value); return fromBignumObj(tkapp, value);
} }
#endif #endif
@ -1366,19 +1396,28 @@ finally:
return NULL; return NULL;
} }
/* Convert the results of a command call into a Python string. */
static PyObject *
Tkapp_UnicodeResult(TkappObject *self)
{
return unicodeFromTclObj(Tcl_GetObjResult(self->interp));
}
/* Convert the results of a command call into a Python objects. */ /* Convert the results of a command call into a Python objects. */
static PyObject* static PyObject *
Tkapp_CallResult(TkappObject *self) Tkapp_ObjectResult(TkappObject *self)
{ {
PyObject *res = NULL; PyObject *res = NULL;
Tcl_Obj *value = Tcl_GetObjResult(self->interp); Tcl_Obj *value = Tcl_GetObjResult(self->interp);
if(self->wantobjects) { if (self->wantobjects) {
/* Not sure whether the IncrRef is necessary, but something /* Not sure whether the IncrRef is necessary, but something
may overwrite the interpreter result while we are may overwrite the interpreter result while we are
converting it. */ converting it. */
Tcl_IncrRefCount(value); Tcl_IncrRefCount(value);
res = FromObj((PyObject*)self, value); res = FromObj(self, value);
Tcl_DecrRefCount(value); Tcl_DecrRefCount(value);
} else { } else {
res = unicodeFromTclObj(value); res = unicodeFromTclObj(value);
@ -1410,15 +1449,13 @@ Tkapp_CallProc(Tkapp_CallEvent *e, int flags)
i = Tcl_EvalObjv(e->self->interp, objc, objv, e->flags); i = Tcl_EvalObjv(e->self->interp, objc, objv, e->flags);
ENTER_PYTHON ENTER_PYTHON
if (i == TCL_ERROR) { if (i == TCL_ERROR) {
*(e->res) = NULL; *(e->res) = Tkinter_Error(e->self);
*(e->exc_type) = NULL;
*(e->exc_tb) = NULL;
*(e->exc_value) = PyObject_CallFunction(
Tkinter_TclError, "s",
Tcl_GetStringResult(e->self->interp));
} }
else { else {
*(e->res) = Tkapp_CallResult(e->self); *(e->res) = Tkapp_ObjectResult(e->self);
}
if (*(e->res) == NULL) {
PyErr_Fetch(e->exc_type, e->exc_value, e->exc_tb);
} }
LEAVE_PYTHON LEAVE_PYTHON
@ -1506,9 +1543,9 @@ Tkapp_Call(PyObject *selfptr, PyObject *args)
ENTER_OVERLAP ENTER_OVERLAP
if (i == TCL_ERROR) if (i == TCL_ERROR)
Tkinter_Error(selfptr); Tkinter_Error(self);
else else
res = Tkapp_CallResult(self); res = Tkapp_ObjectResult(self);
LEAVE_OVERLAP_TCL LEAVE_OVERLAP_TCL
@ -1540,9 +1577,9 @@ _tkinter_tkapp_eval_impl(TkappObject *self, const char *script)
err = Tcl_Eval(Tkapp_Interp(self), script); err = Tcl_Eval(Tkapp_Interp(self), script);
ENTER_OVERLAP ENTER_OVERLAP
if (err == TCL_ERROR) if (err == TCL_ERROR)
res = Tkinter_Error((PyObject *)self); res = Tkinter_Error(self);
else else
res = unicodeFromTclString(Tkapp_Result(self)); res = Tkapp_UnicodeResult(self);
LEAVE_OVERLAP_TCL LEAVE_OVERLAP_TCL
return res; return res;
} }
@ -1569,9 +1606,9 @@ _tkinter_tkapp_evalfile_impl(TkappObject *self, const char *fileName)
err = Tcl_EvalFile(Tkapp_Interp(self), fileName); err = Tcl_EvalFile(Tkapp_Interp(self), fileName);
ENTER_OVERLAP ENTER_OVERLAP
if (err == TCL_ERROR) if (err == TCL_ERROR)
res = Tkinter_Error((PyObject *)self); res = Tkinter_Error(self);
else else
res = unicodeFromTclString(Tkapp_Result(self)); res = Tkapp_UnicodeResult(self);
LEAVE_OVERLAP_TCL LEAVE_OVERLAP_TCL
return res; return res;
} }
@ -1598,9 +1635,9 @@ _tkinter_tkapp_record_impl(TkappObject *self, const char *script)
err = Tcl_RecordAndEval(Tkapp_Interp(self), script, TCL_NO_EVAL); err = Tcl_RecordAndEval(Tkapp_Interp(self), script, TCL_NO_EVAL);
ENTER_OVERLAP ENTER_OVERLAP
if (err == TCL_ERROR) if (err == TCL_ERROR)
res = Tkinter_Error((PyObject *)self); res = Tkinter_Error(self);
else else
res = unicodeFromTclString(Tkapp_Result(self)); res = Tkapp_UnicodeResult(self);
LEAVE_OVERLAP_TCL LEAVE_OVERLAP_TCL
return res; return res;
} }
@ -1631,13 +1668,13 @@ _tkinter_tkapp_adderrorinfo_impl(TkappObject *self, const char *msg)
/** Tcl Variable **/ /** Tcl Variable **/
typedef PyObject* (*EventFunc)(PyObject*, PyObject *args, int flags); typedef PyObject* (*EventFunc)(TkappObject *, PyObject *, int);
TCL_DECLARE_MUTEX(var_mutex) TCL_DECLARE_MUTEX(var_mutex)
typedef struct VarEvent { typedef struct VarEvent {
Tcl_Event ev; /* must be first */ Tcl_Event ev; /* must be first */
PyObject *self; TkappObject *self;
PyObject *args; PyObject *args;
int flags; int flags;
EventFunc func; EventFunc func;
@ -1692,7 +1729,7 @@ varname_converter(PyObject *in, void *_out)
return 1; return 1;
} }
if (PyTclObject_Check(in)) { if (PyTclObject_Check(in)) {
*out = PyTclObject_TclString(in); *out = Tcl_GetString(((PyTclObject *)in)->value);
return 1; return 1;
} }
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
@ -1750,7 +1787,7 @@ var_invoke(EventFunc func, PyObject *selfptr, PyObject *args, int flags)
PyErr_NoMemory(); PyErr_NoMemory();
return NULL; return NULL;
} }
ev->self = selfptr; ev->self = self;
ev->args = args; ev->args = args;
ev->flags = flags; ev->flags = flags;
ev->func = func; ev->func = func;
@ -1770,11 +1807,11 @@ var_invoke(EventFunc func, PyObject *selfptr, PyObject *args, int flags)
return res; return res;
} }
/* Tcl is not threaded, or this is the interpreter thread. */ /* Tcl is not threaded, or this is the interpreter thread. */
return func(selfptr, args, flags); return func(self, args, flags);
} }
static PyObject * static PyObject *
SetVar(PyObject *self, PyObject *args, int flags) SetVar(TkappObject *self, PyObject *args, int flags)
{ {
const char *name1, *name2; const char *name1, *name2;
PyObject *newValue; PyObject *newValue;
@ -1843,7 +1880,7 @@ Tkapp_GlobalSetVar(PyObject *self, PyObject *args)
static PyObject * static PyObject *
GetVar(PyObject *self, PyObject *args, int flags) GetVar(TkappObject *self, PyObject *args, int flags)
{ {
const char *name1, *name2=NULL; const char *name1, *name2=NULL;
PyObject *res = NULL; PyObject *res = NULL;
@ -1858,10 +1895,9 @@ GetVar(PyObject *self, PyObject *args, int flags)
tres = Tcl_GetVar2Ex(Tkapp_Interp(self), name1, name2, flags); tres = Tcl_GetVar2Ex(Tkapp_Interp(self), name1, name2, flags);
ENTER_OVERLAP ENTER_OVERLAP
if (tres == NULL) { if (tres == NULL) {
PyErr_SetString(Tkinter_TclError, Tkinter_Error(self);
Tcl_GetStringResult(Tkapp_Interp(self)));
} else { } else {
if (((TkappObject*)self)->wantobjects) { if (self->wantobjects) {
res = FromObj(self, tres); res = FromObj(self, tres);
} }
else { else {
@ -1887,7 +1923,7 @@ Tkapp_GlobalGetVar(PyObject *self, PyObject *args)
static PyObject * static PyObject *
UnsetVar(PyObject *self, PyObject *args, int flags) UnsetVar(TkappObject *self, PyObject *args, int flags)
{ {
char *name1, *name2=NULL; char *name1, *name2=NULL;
int code; int code;
@ -1959,7 +1995,7 @@ _tkinter_tkapp_getint(TkappObject *self, PyObject *arg)
CHECK_STRING_LENGTH(s); CHECK_STRING_LENGTH(s);
value = Tcl_NewStringObj(s, -1); value = Tcl_NewStringObj(s, -1);
if (value == NULL) if (value == NULL)
return Tkinter_Error((PyObject *)self); return Tkinter_Error(self);
} }
/* Don't use Tcl_GetInt() because it returns ambiguous result for value /* Don't use Tcl_GetInt() because it returns ambiguous result for value
in ranges -2**32..-2**31-1 and 2**31..2**32-1 (on 32-bit platform). in ranges -2**32..-2**31-1 and 2**31..2**32-1 (on 32-bit platform).
@ -1968,14 +2004,14 @@ _tkinter_tkapp_getint(TkappObject *self, PyObject *arg)
value in ranges -2**64..-2**63-1 and 2**63..2**64-1 (on 32-bit platform). value in ranges -2**64..-2**63-1 and 2**63..2**64-1 (on 32-bit platform).
*/ */
#ifdef HAVE_LIBTOMMAMTH #ifdef HAVE_LIBTOMMAMTH
result = fromBignumObj((PyObject *)self, value); result = fromBignumObj(self, value);
#else #else
result = fromWideIntObj((PyObject *)self, value); result = fromWideIntObj(self, value);
#endif #endif
Tcl_DecrRefCount(value); Tcl_DecrRefCount(value);
if (result != NULL || PyErr_Occurred()) if (result != NULL || PyErr_Occurred())
return result; return result;
return Tkinter_Error((PyObject *)self); return Tkinter_Error(self);
} }
/*[clinic input] /*[clinic input]
@ -2006,7 +2042,7 @@ _tkinter_tkapp_getdouble(TkappObject *self, PyObject *arg)
if (Tcl_GetDoubleFromObj(Tkapp_Interp(self), if (Tcl_GetDoubleFromObj(Tkapp_Interp(self),
((PyTclObject*)arg)->value, ((PyTclObject*)arg)->value,
&v) == TCL_ERROR) &v) == TCL_ERROR)
return Tkinter_Error((PyObject *)self); return Tkinter_Error(self);
return PyFloat_FromDouble(v); return PyFloat_FromDouble(v);
} }
@ -2014,7 +2050,7 @@ _tkinter_tkapp_getdouble(TkappObject *self, PyObject *arg)
return NULL; return NULL;
CHECK_STRING_LENGTH(s); CHECK_STRING_LENGTH(s);
if (Tcl_GetDouble(Tkapp_Interp(self), s, &v) == TCL_ERROR) if (Tcl_GetDouble(Tkapp_Interp(self), s, &v) == TCL_ERROR)
return Tkinter_Error((PyObject *)self); return Tkinter_Error(self);
return PyFloat_FromDouble(v); return PyFloat_FromDouble(v);
} }
@ -2041,7 +2077,7 @@ _tkinter_tkapp_getboolean(TkappObject *self, PyObject *arg)
if (Tcl_GetBooleanFromObj(Tkapp_Interp(self), if (Tcl_GetBooleanFromObj(Tkapp_Interp(self),
((PyTclObject*)arg)->value, ((PyTclObject*)arg)->value,
&v) == TCL_ERROR) &v) == TCL_ERROR)
return Tkinter_Error((PyObject *)self); return Tkinter_Error(self);
return PyBool_FromLong(v); return PyBool_FromLong(v);
} }
@ -2049,7 +2085,7 @@ _tkinter_tkapp_getboolean(TkappObject *self, PyObject *arg)
return NULL; return NULL;
CHECK_STRING_LENGTH(s); CHECK_STRING_LENGTH(s);
if (Tcl_GetBoolean(Tkapp_Interp(self), s, &v) == TCL_ERROR) if (Tcl_GetBoolean(Tkapp_Interp(self), s, &v) == TCL_ERROR)
return Tkinter_Error((PyObject *)self); return Tkinter_Error(self);
return PyBool_FromLong(v); return PyBool_FromLong(v);
} }
@ -2075,9 +2111,9 @@ _tkinter_tkapp_exprstring_impl(TkappObject *self, const char *s)
retval = Tcl_ExprString(Tkapp_Interp(self), s); retval = Tcl_ExprString(Tkapp_Interp(self), s);
ENTER_OVERLAP ENTER_OVERLAP
if (retval == TCL_ERROR) if (retval == TCL_ERROR)
res = Tkinter_Error((PyObject *)self); res = Tkinter_Error(self);
else else
res = unicodeFromTclString(Tkapp_Result(self)); res = Tkapp_UnicodeResult(self);
LEAVE_OVERLAP_TCL LEAVE_OVERLAP_TCL
return res; return res;
} }
@ -2105,7 +2141,7 @@ _tkinter_tkapp_exprlong_impl(TkappObject *self, const char *s)
retval = Tcl_ExprLong(Tkapp_Interp(self), s, &v); retval = Tcl_ExprLong(Tkapp_Interp(self), s, &v);
ENTER_OVERLAP ENTER_OVERLAP
if (retval == TCL_ERROR) if (retval == TCL_ERROR)
res = Tkinter_Error((PyObject *)self); res = Tkinter_Error(self);
else else
res = PyLong_FromLong(v); res = PyLong_FromLong(v);
LEAVE_OVERLAP_TCL LEAVE_OVERLAP_TCL
@ -2136,7 +2172,7 @@ _tkinter_tkapp_exprdouble_impl(TkappObject *self, const char *s)
ENTER_OVERLAP ENTER_OVERLAP
PyFPE_END_PROTECT(retval) PyFPE_END_PROTECT(retval)
if (retval == TCL_ERROR) if (retval == TCL_ERROR)
res = Tkinter_Error((PyObject *)self); res = Tkinter_Error(self);
else else
res = PyFloat_FromDouble(v); res = PyFloat_FromDouble(v);
LEAVE_OVERLAP_TCL LEAVE_OVERLAP_TCL
@ -2165,7 +2201,7 @@ _tkinter_tkapp_exprboolean_impl(TkappObject *self, const char *s)
retval = Tcl_ExprBoolean(Tkapp_Interp(self), s, &v); retval = Tcl_ExprBoolean(Tkapp_Interp(self), s, &v);
ENTER_OVERLAP ENTER_OVERLAP
if (retval == TCL_ERROR) if (retval == TCL_ERROR)
res = Tkinter_Error((PyObject *)self); res = Tkinter_Error(self);
else else
res = PyLong_FromLong(v); res = PyLong_FromLong(v);
LEAVE_OVERLAP_TCL LEAVE_OVERLAP_TCL
@ -2198,12 +2234,12 @@ _tkinter_tkapp_splitlist(TkappObject *self, PyObject *arg)
if (Tcl_ListObjGetElements(Tkapp_Interp(self), if (Tcl_ListObjGetElements(Tkapp_Interp(self),
((PyTclObject*)arg)->value, ((PyTclObject*)arg)->value,
&objc, &objv) == TCL_ERROR) { &objc, &objv) == TCL_ERROR) {
return Tkinter_Error((PyObject *)self); return Tkinter_Error(self);
} }
if (!(v = PyTuple_New(objc))) if (!(v = PyTuple_New(objc)))
return NULL; return NULL;
for (i = 0; i < objc; i++) { for (i = 0; i < objc; i++) {
PyObject *s = FromObj((PyObject*)self, objv[i]); PyObject *s = FromObj(self, objv[i]);
if (!s) { if (!s) {
Py_DECREF(v); Py_DECREF(v);
return NULL; return NULL;
@ -2231,7 +2267,7 @@ _tkinter_tkapp_splitlist(TkappObject *self, PyObject *arg)
if (Tcl_SplitList(Tkapp_Interp(self), list, if (Tcl_SplitList(Tkapp_Interp(self), list,
&argc, &argv) == TCL_ERROR) { &argc, &argv) == TCL_ERROR) {
PyMem_Free(list); PyMem_Free(list);
return Tkinter_Error((PyObject *)self); return Tkinter_Error(self);
} }
if (!(v = PyTuple_New(argc))) if (!(v = PyTuple_New(argc)))
@ -2275,16 +2311,16 @@ _tkinter_tkapp_split(TkappObject *self, PyObject *arg)
int i; int i;
if (Tcl_ListObjGetElements(Tkapp_Interp(self), value, if (Tcl_ListObjGetElements(Tkapp_Interp(self), value,
&objc, &objv) == TCL_ERROR) { &objc, &objv) == TCL_ERROR) {
return FromObj((PyObject*)self, value); return FromObj(self, value);
} }
if (objc == 0) if (objc == 0)
return PyUnicode_FromString(""); return PyUnicode_FromString("");
if (objc == 1) if (objc == 1)
return FromObj((PyObject*)self, objv[0]); return FromObj(self, objv[0]);
if (!(v = PyTuple_New(objc))) if (!(v = PyTuple_New(objc)))
return NULL; return NULL;
for (i = 0; i < objc; i++) { for (i = 0; i < objc; i++) {
PyObject *s = FromObj((PyObject*)self, objv[i]); PyObject *s = FromObj(self, objv[i]);
if (!s) { if (!s) {
Py_DECREF(v); Py_DECREF(v);
return NULL; return NULL;
@ -2331,34 +2367,31 @@ PythonCmd_Error(Tcl_Interp *interp)
* function or method. * function or method.
*/ */
static int static int
PythonCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[]) PythonCmd(ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[])
{ {
PythonCmd_ClientData *data = (PythonCmd_ClientData *)clientData; PythonCmd_ClientData *data = (PythonCmd_ClientData *)clientData;
PyObject *func, *arg, *res; PyObject *args, *res;
int i, rv; int i;
Tcl_Obj *obj_res; Tcl_Obj *obj_res;
ENTER_PYTHON ENTER_PYTHON
/* TBD: no error checking here since we know, via the /* Create argument tuple (objv1, ..., objvN) */
* Tkapp_CreateCommand() that the client data is a two-tuple if (!(args = PyTuple_New(objc - 1)))
*/
func = data->func;
/* Create argument list (argv1, ..., argvN) */
if (!(arg = PyTuple_New(argc - 1)))
return PythonCmd_Error(interp); return PythonCmd_Error(interp);
for (i = 0; i < (argc - 1); i++) { for (i = 0; i < (objc - 1); i++) {
PyObject *s = unicodeFromTclString(argv[i + 1]); PyObject *s = unicodeFromTclObj(objv[i + 1]);
if (!s) { if (!s) {
Py_DECREF(arg); Py_DECREF(args);
return PythonCmd_Error(interp); return PythonCmd_Error(interp);
} }
PyTuple_SET_ITEM(arg, i, s); PyTuple_SET_ITEM(args, i, s);
} }
res = PyObject_Call(func, arg, NULL);
Py_DECREF(arg); res = PyObject_Call(data->func, args, NULL);
Py_DECREF(args);
if (res == NULL) if (res == NULL)
return PythonCmd_Error(interp); return PythonCmd_Error(interp);
@ -2368,18 +2401,15 @@ PythonCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[
Py_DECREF(res); Py_DECREF(res);
return PythonCmd_Error(interp); return PythonCmd_Error(interp);
} }
else { Tcl_SetObjResult(interp, obj_res);
Tcl_SetObjResult(interp, obj_res);
rv = TCL_OK;
}
Py_DECREF(res); Py_DECREF(res);
LEAVE_PYTHON LEAVE_PYTHON
return rv; return TCL_OK;
} }
static void static void
PythonCmdDelete(ClientData clientData) PythonCmdDelete(ClientData clientData)
{ {
@ -2411,7 +2441,7 @@ static int
Tkapp_CommandProc(CommandEvent *ev, int flags) Tkapp_CommandProc(CommandEvent *ev, int flags)
{ {
if (ev->create) if (ev->create)
*ev->status = Tcl_CreateCommand( *ev->status = Tcl_CreateObjCommand(
ev->interp, ev->name, PythonCmd, ev->interp, ev->name, PythonCmd,
ev->data, PythonCmdDelete) == NULL; ev->data, PythonCmdDelete) == NULL;
else else
@ -2477,7 +2507,7 @@ _tkinter_tkapp_createcommand_impl(TkappObject *self, const char *name,
else else
{ {
ENTER_TCL ENTER_TCL
err = Tcl_CreateCommand( err = Tcl_CreateObjCommand(
Tkapp_Interp(self), name, PythonCmd, Tkapp_Interp(self), name, PythonCmd,
(ClientData)data, PythonCmdDelete) == NULL; (ClientData)data, PythonCmdDelete) == NULL;
LEAVE_TCL LEAVE_TCL
@ -2953,9 +2983,9 @@ _tkinter_tkapp_loadtk_impl(TkappObject *self)
if (err == TCL_ERROR) { if (err == TCL_ERROR) {
/* This sets an exception, but we cannot return right /* This sets an exception, but we cannot return right
away because we need to exit the overlap first. */ away because we need to exit the overlap first. */
Tkinter_Error((PyObject *)self); Tkinter_Error(self);
} else { } else {
_tk_exists = Tkapp_Result(self); _tk_exists = Tcl_GetStringResult(Tkapp_Interp(self));
} }
LEAVE_OVERLAP_TCL LEAVE_OVERLAP_TCL
if (err == TCL_ERROR) { if (err == TCL_ERROR) {
@ -2963,8 +2993,7 @@ _tkinter_tkapp_loadtk_impl(TkappObject *self)
} }
if (_tk_exists == NULL || strcmp(_tk_exists, "1") != 0) { if (_tk_exists == NULL || strcmp(_tk_exists, "1") != 0) {
if (Tk_Init(interp) == TCL_ERROR) { if (Tk_Init(interp) == TCL_ERROR) {
PyErr_SetString(Tkinter_TclError, Tkinter_Error(self);
Tcl_GetStringResult(Tkapp_Interp(self)));
#ifdef TKINTER_PROTECT_LOADTK #ifdef TKINTER_PROTECT_LOADTK
tk_load_failed = 1; tk_load_failed = 1;
#endif #endif