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

Miss Islington (bot) webhook-mailer at python.org
Fri Oct 4 06:28:57 EDT 2019


https://github.com/python/cpython/commit/6c3fbbc177f5e1867ab09a315dbf58554a80accd
commit: 6c3fbbc177f5e1867ab09a315dbf58554a80accd
branch: 3.7
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2019-10-04T03:28:53-07:00
summary:

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).
(cherry picked from commit 06cb94bc8419b9a24df6b0d724fcd8e40c6971d6)

Co-authored-by: Serhiy Storchaka <storchaka at gmail.com>

files:
A Misc/NEWS.d/next/Library/2019-09-29-22-47-37.bpo-13153.0mO9qR.rst
M Lib/idlelib/editor.py
M Lib/idlelib/idle_test/test_editor.py
M Lib/idlelib/pyshell.py
M Lib/idlelib/runscript.py
M Lib/test/test_tcl.py
M Lib/tkinter/test/test_tkinter/test_misc.py
M Lib/tkinter/test/test_ttk/test_widgets.py
M Modules/_tkinter.c

diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py
index b969f8c493b27..adeed74059f84 100644
--- a/Lib/idlelib/editor.py
+++ b/Lib/idlelib/editor.py
@@ -358,21 +358,6 @@ def set_width(self):
             Font(text, font=text.cget('font')).measure('0')
         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):
         dirname, basename = self.io.defaultfilename()
         self.flist.new(dirname)
@@ -963,10 +948,8 @@ def update_recent_files_list(self, new_file=None):
             menu.delete(0, END)  # clear, and rebuild:
             for i, file_name in enumerate(rf_list):
                 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)
-                menu.add_command(label=ulchars[i] + " " + ufile_name,
+                menu.add_command(label=ulchars[i] + " " + file_name,
                                  command=callback,
                                  underline=0)
 
@@ -1004,16 +987,10 @@ def reset_undo(self):
 
     def short_title(self):
         filename = self.io.filename
-        if filename:
-            filename = os.path.basename(filename)
-        else:
-            filename = "untitled"
-        # return unicode string to display non-ASCII chars correctly
-        return self._filename_to_unicode(filename)
+        return os.path.basename(filename) if filename else "untitled"
 
     def long_title(self):
-        # return unicode string to display non-ASCII chars correctly
-        return self._filename_to_unicode(self.io.filename or "")
+        return self.io.filename or ""
 
     def center_insert_event(self, event):
         self.center()
diff --git a/Lib/idlelib/idle_test/test_editor.py b/Lib/idlelib/idle_test/test_editor.py
index 4af4ff0242d74..240db71747a28 100644
--- a/Lib/idlelib/idle_test/test_editor.py
+++ b/Lib/idlelib/idle_test/test_editor.py
@@ -30,18 +30,6 @@ def test_init(self):
         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):
     def test_empty_lines(self):
         for tabwidth in [1, 2, 4, 6, 8]:
diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py
index 2e4dfad401221..bc87d24eaabf8 100755
--- a/Lib/idlelib/pyshell.py
+++ b/Lib/idlelib/pyshell.py
@@ -679,14 +679,6 @@ def runsource(self, source):
         self.more = 0
         # at the moment, InteractiveInterpreter expects 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,
         # which is overridden (see below)
         return InteractiveInterpreter.runsource(self, source, filename)
@@ -1298,16 +1290,6 @@ def resetoutput(self):
         self.set_line_and_column()
 
     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:
             self.text.mark_gravity("iomark", "right")
             count = OutputWindow.write(self, s, tags, "iomark")
diff --git a/Lib/idlelib/runscript.py b/Lib/idlelib/runscript.py
index de73bf8458151..e99d0d2e027fc 100644
--- a/Lib/idlelib/runscript.py
+++ b/Lib/idlelib/runscript.py
@@ -147,8 +147,7 @@ def _run_module_event(self, event, *, customize=False):
         interp = self.shell.interp
         if pyshell.use_subprocess and restart:
             interp.restart_subprocess(
-                    with_cwd=False, filename=
-                    self.editwin._filename_to_unicode(filename))
+                    with_cwd=False, filename=filename)
         dirname = os.path.dirname(filename)
         argv = [filename]
         if self.cli_args:
diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py
index 80f1668bcecab..3183ea850f73b 100644
--- a/Lib/test/test_tcl.py
+++ b/Lib/test/test_tcl.py
@@ -429,9 +429,12 @@ def passValue(value):
         self.assertEqual(passValue(False), False if self.wantobjects else '0')
         self.assertEqual(passValue('string'), 'string')
         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\xbd'), 'str\x00ing\xbd')
         self.assertEqual(passValue('str\x00ing\u20ac'), 'str\x00ing\u20ac')
+        self.assertEqual(passValue('str\x00ing\U0001f4bb'),
+                         'str\x00ing\U0001f4bb')
         self.assertEqual(passValue(b'str\x00ing'),
                          b'str\x00ing' if self.wantobjects else 'str\x00ing')
         self.assertEqual(passValue(b'str\xc0\x80ing'),
@@ -490,6 +493,7 @@ def float_eq(actual, expected):
         check('string')
         check('string\xbd')
         check('string\u20ac')
+        check('string\U0001f4bb')
         check('')
         check(b'string', 'string')
         check(b'string\xe2\x82\xac', 'string\xe2\x82\xac')
@@ -531,6 +535,7 @@ def test_splitlist(self):
             ('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 \U0001f4bb', ('a', '\U0001f4bb')),
             (b'a \xe2\x82\xac', ('a', '\u20ac')),
             (b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')),
             ('a {b c}', ('a', 'b c')),
diff --git a/Lib/tkinter/test/test_tkinter/test_misc.py b/Lib/tkinter/test/test_tkinter/test_misc.py
index 1d1a3c29f6bc4..236cae0e9fac4 100644
--- a/Lib/tkinter/test/test_tkinter/test_misc.py
+++ b/Lib/tkinter/test/test_tkinter/test_misc.py
@@ -156,6 +156,28 @@ def callback():
         with self.assertRaises(tkinter.TclError):
             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, )
 
diff --git a/Lib/tkinter/test/test_ttk/test_widgets.py b/Lib/tkinter/test/test_ttk/test_widgets.py
index ba9e3b54f78e7..9667e05cab12c 100644
--- a/Lib/tkinter/test/test_ttk/test_widgets.py
+++ b/Lib/tkinter/test/test_ttk/test_widgets.py
@@ -489,8 +489,7 @@ def check_get_current(getval, currval):
                         expected=('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', '',
-                        expected='' if get_tk_patchlevel() < (8, 5, 10) else ())
+        self.checkParam(self.combo, 'values', '')
 
         self.combo['values'] = ['a', 1, 'c']
 
@@ -1245,12 +1244,7 @@ def test_values(self):
                         expected=('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',
-            '',
-            expected='' if get_tk_patchlevel() < (8, 5, 10) else ()
-        )
+        self.checkParam(self.spin, 'values', '')
 
         self.spin['values'] = ['a', 1, 'c']
 
@@ -1308,8 +1302,7 @@ def test_columns(self):
         self.checkParam(widget, 'columns', 'a b c',
                         expected=('a', 'b', 'c'))
         self.checkParam(widget, 'columns', ('a', 'b', 'c'))
-        self.checkParam(widget, 'columns', (),
-                        expected='' if get_tk_patchlevel() < (8, 5, 10) else ())
+        self.checkParam(widget, 'columns', '')
 
     def test_displaycolumns(self):
         widget = self.create()
diff --git a/Misc/NEWS.d/next/Library/2019-09-29-22-47-37.bpo-13153.0mO9qR.rst b/Misc/NEWS.d/next/Library/2019-09-29-22-47-37.bpo-13153.0mO9qR.rst
new file mode 100644
index 0000000000000..6f9561539d8d3
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-09-29-22-47-37.bpo-13153.0mO9qR.rst
@@ -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).
diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c
index a96924c9c6e71..8ea302ea639f9 100644
--- a/Modules/_tkinter.c
+++ b/Modules/_tkinter.c
@@ -96,6 +96,24 @@ Copyright (C) 1994 Steen Lumholt.
 
 #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
 #include <conio.h>
 #define WAIT_FOR_STDIN
@@ -290,7 +308,6 @@ typedef struct {
 } TkappObject;
 
 #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", \
 (void *) v, Py_REFCNT(v)))
@@ -311,10 +328,16 @@ static int tk_load_failed = 0;
 #endif
 
 
+static PyObject *Tkapp_UnicodeResult(TkappObject *);
+
 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;
 }
 
@@ -368,30 +391,35 @@ static PyObject *
 unicodeFromTclStringAndSize(const char *s, Py_ssize_t size)
 {
     PyObject *r = PyUnicode_DecodeUTF8(s, size, NULL);
-    if (!r && PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) {
-        /* Tcl encodes null character as \xc0\x80 */
-        if (memchr(s, '\xc0', size)) {
-            char *buf, *q;
-            const char *e = s + size;
-            PyErr_Clear();
-            q = buf = (char *)PyMem_Malloc(size);
-            if (buf == NULL) {
-                PyErr_NoMemory();
-                return NULL;
-            }
-            while (s != e) {
-                if (s + 1 != e && s[0] == '\xc0' && s[1] == '\x80') {
-                    *q++ = '\0';
-                    s += 2;
-                }
-                else
-                    *q++ = *s++;
+    if (r != NULL || !PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) {
+        return r;
+    }
+
+    char *buf = NULL;
+    PyErr_Clear();
+    /* Tcl encodes null character as \xc0\x80 */
+    if (memchr(s, '\xc0', size)) {
+        char *q;
+        const char *e = s + size;
+        q = buf = (char *)PyMem_Malloc(size);
+        if (buf == NULL) {
+            PyErr_NoMemory();
+            return NULL;
+        }
+        while (s != e) {
+            if (s + 1 != e && s[0] == '\xc0' && s[1] == '\x80') {
+                *q++ = '\0';
+                s += 2;
             }
-            s = buf;
-            size = q - s;
-            r = PyUnicode_DecodeUTF8(s, size, NULL);
-            PyMem_Free(buf);
+            else
+                *q++ = *s++;
         }
+        s = buf;
+        size = q - s;
+    }
+    r = PyUnicode_DecodeUTF8(s, size, "surrogateescape");
+    if (buf != NULL) {
+        PyMem_Free(buf);
     }
     return r;
 }
@@ -406,8 +434,21 @@ static PyObject *
 unicodeFromTclObj(Tcl_Obj *value)
 {
     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);
+#endif
 }
 
 
@@ -747,7 +788,7 @@ Tkapp_New(const char *screenName, const char *className,
 #endif
 
     if (Tcl_AppInit(v->interp) != TCL_OK) {
-        PyObject *result = Tkinter_Error((PyObject *)v);
+        PyObject *result = Tkinter_Error(v);
 #ifdef TKINTER_PROTECT_LOADTK
         if (wantTk) {
             const char *_tkinter_tk_failed;
@@ -819,12 +860,6 @@ PyTclObject_dealloc(PyTclObject *self)
     Py_DECREF(tp);
 }
 
-static const char *
-PyTclObject_TclString(PyObject *self)
-{
-    return Tcl_GetString(((PyTclObject*)self)->value);
-}
-
 /* Like _str, but create Unicode if necessary. */
 PyDoc_STRVAR(PyTclObject_string__doc__,
 "the string representation of this object, either as str or bytes");
@@ -1050,53 +1085,51 @@ AsObj(PyObject *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)
             return NULL;
 
-        inbuf = PyUnicode_DATA(value);
-        size = PyUnicode_GET_LENGTH(value);
-        if (size == 0)
-            return Tcl_NewUnicodeObj((const void *)"", 0);
+        Py_ssize_t size = PyUnicode_GET_LENGTH(value);
+        if (size == 0) {
+            return Tcl_NewStringObj("", 0);
+        }
         if (!CHECK_SIZE(size, sizeof(Tcl_UniChar))) {
             PyErr_SetString(PyExc_OverflowError, "string is too long");
             return NULL;
         }
-        kind = PyUnicode_KIND(value);
-        if (kind == sizeof(Tcl_UniChar))
-            return Tcl_NewUnicodeObj(inbuf, (int)size);
-        allocsize = ((size_t)size) * sizeof(Tcl_UniChar);
-        outbuf = (Tcl_UniChar*)PyMem_Malloc(allocsize);
-        /* Else overflow occurred, and we take the next exit */
-        if (!outbuf) {
-            PyErr_NoMemory();
-            return NULL;
+        if (PyUnicode_IS_ASCII(value)) {
+            return Tcl_NewStringObj((const char *)PyUnicode_DATA(value),
+                                    (int)size);
         }
-        for (i = 0; i < size; i++) {
-            Py_UCS4 ch = PyUnicode_READ(kind, inbuf, i);
-            /* We cannot test for sizeof(Tcl_UniChar) directly,
-               so we test for UTF-8 size instead. */
-#if TCL_UTF_MAX == 3
-            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;
-            }
+
+        PyObject *encoded;
+#if USE_TCL_UNICODE
+        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
-            outbuf[i] = ch;
+        if (!encoded) {
+            return NULL;
         }
-        result = Tcl_NewUnicodeObj(outbuf, (int)size);
-        PyMem_Free(outbuf);
+        size = PyBytes_GET_SIZE(encoded);
+        if (size > INT_MAX) {
+            Py_DECREF(encoded);
+            PyErr_SetString(PyExc_OverflowError, "string is too long");
+            return NULL;
+        }
+#if USE_TCL_UNICODE
+        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;
     }
 
@@ -1115,7 +1148,7 @@ AsObj(PyObject *value)
 }
 
 static PyObject *
-fromBoolean(PyObject* tkapp, Tcl_Obj *value)
+fromBoolean(TkappObject *tkapp, Tcl_Obj *value)
 {
     int boolValue;
     if (Tcl_GetBooleanFromObj(Tkapp_Interp(tkapp), value, &boolValue) == TCL_ERROR)
@@ -1124,7 +1157,7 @@ fromBoolean(PyObject* tkapp, Tcl_Obj *value)
 }
 
 static PyObject*
-fromWideIntObj(PyObject* tkapp, Tcl_Obj *value)
+fromWideIntObj(TkappObject *tkapp, Tcl_Obj *value)
 {
         Tcl_WideInt wideValue;
         if (Tcl_GetWideIntFromObj(Tkapp_Interp(tkapp), value, &wideValue) == TCL_OK) {
@@ -1140,7 +1173,7 @@ fromWideIntObj(PyObject* tkapp, Tcl_Obj *value)
 
 #ifdef HAVE_LIBTOMMAMTH
 static PyObject*
-fromBignumObj(PyObject* tkapp, Tcl_Obj *value)
+fromBignumObj(TkappObject *tkapp, Tcl_Obj *value)
 {
     mp_int bigValue;
     unsigned long numBytes;
@@ -1176,32 +1209,31 @@ fromBignumObj(PyObject* tkapp, Tcl_Obj *value)
 #endif
 
 static PyObject*
-FromObj(PyObject* tkapp, Tcl_Obj *value)
+FromObj(TkappObject *tkapp, Tcl_Obj *value)
 {
     PyObject *result = NULL;
-    TkappObject *app = (TkappObject*)tkapp;
     Tcl_Interp *interp = Tkapp_Interp(tkapp);
 
     if (value->typePtr == NULL) {
-        return unicodeFromTclStringAndSize(value->bytes, value->length);
+        return unicodeFromTclObj(value);
     }
 
-    if (value->typePtr == app->BooleanType ||
-        value->typePtr == app->OldBooleanType) {
+    if (value->typePtr == tkapp->BooleanType ||
+        value->typePtr == tkapp->OldBooleanType) {
         return fromBoolean(tkapp, value);
     }
 
-    if (value->typePtr == app->ByteArrayType) {
+    if (value->typePtr == tkapp->ByteArrayType) {
         int size;
         char *data = (char*)Tcl_GetByteArrayFromObj(value, &size);
         return PyBytes_FromStringAndSize(data, size);
     }
 
-    if (value->typePtr == app->DoubleType) {
+    if (value->typePtr == tkapp->DoubleType) {
         return PyFloat_FromDouble(value->internalRep.doubleValue);
     }
 
-    if (value->typePtr == app->IntType) {
+    if (value->typePtr == tkapp->IntType) {
         long longValue;
         if (Tcl_GetLongFromObj(interp, value, &longValue) == TCL_OK)
             return PyLong_FromLong(longValue);
@@ -1209,8 +1241,8 @@ FromObj(PyObject* tkapp, Tcl_Obj *value)
            fall through to wideInt handling. */
     }
 
-    if (value->typePtr == app->IntType ||
-        value->typePtr == app->WideIntType) {
+    if (value->typePtr == tkapp->IntType ||
+        value->typePtr == tkapp->WideIntType) {
         result = fromWideIntObj(tkapp, value);
         if (result != NULL || PyErr_Occurred())
             return result;
@@ -1220,14 +1252,14 @@ FromObj(PyObject* tkapp, Tcl_Obj *value)
     }
 
 #ifdef HAVE_LIBTOMMAMTH
-    if (value->typePtr == app->IntType ||
-        value->typePtr == app->WideIntType ||
-        value->typePtr == app->BignumType) {
+    if (value->typePtr == tkapp->IntType ||
+        value->typePtr == tkapp->WideIntType ||
+        value->typePtr == tkapp->BignumType) {
         return fromBignumObj(tkapp, value);
     }
 #endif
 
-    if (value->typePtr == app->ListType) {
+    if (value->typePtr == tkapp->ListType) {
         int size;
         int i, status;
         PyObject *elem;
@@ -1255,30 +1287,28 @@ FromObj(PyObject* tkapp, Tcl_Obj *value)
         return result;
     }
 
-    if (value->typePtr == app->ProcBodyType) {
+    if (value->typePtr == tkapp->ProcBodyType) {
       /* fall through: return tcl object. */
     }
 
-    if (value->typePtr == app->StringType) {
-        return PyUnicode_FromKindAndData(
-            sizeof(Tcl_UniChar), Tcl_GetUnicode(value),
-            Tcl_GetCharLength(value));
+    if (value->typePtr == tkapp->StringType) {
+        return unicodeFromTclObj(value);
     }
 
 #if TK_HEX_VERSION >= 0x08050000
-    if (app->BooleanType == NULL &&
+    if (tkapp->BooleanType == NULL &&
         strcmp(value->typePtr->name, "booleanString") == 0) {
         /* booleanString type is not registered in Tcl */
-        app->BooleanType = value->typePtr;
+        tkapp->BooleanType = value->typePtr;
         return fromBoolean(tkapp, value);
     }
 #endif
 
 #ifdef HAVE_LIBTOMMAMTH
-    if (app->BignumType == NULL &&
+    if (tkapp->BignumType == NULL &&
         strcmp(value->typePtr->name, "bignum") == 0) {
         /* bignum type is not registered in Tcl */
-        app->BignumType = value->typePtr;
+        tkapp->BignumType = value->typePtr;
         return fromBignumObj(tkapp, value);
     }
 #endif
@@ -1368,19 +1398,28 @@ Tkapp_CallArgs(PyObject *args, Tcl_Obj** objStore, int *pobjc)
     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. */
 
-static PyObject*
-Tkapp_CallResult(TkappObject *self)
+static PyObject *
+Tkapp_ObjectResult(TkappObject *self)
 {
     PyObject *res = NULL;
     Tcl_Obj *value = Tcl_GetObjResult(self->interp);
-    if(self->wantobjects) {
+    if (self->wantobjects) {
         /* Not sure whether the IncrRef is necessary, but something
            may overwrite the interpreter result while we are
            converting it. */
         Tcl_IncrRefCount(value);
-        res = FromObj((PyObject*)self, value);
+        res = FromObj(self, value);
         Tcl_DecrRefCount(value);
     } else {
         res = unicodeFromTclObj(value);
@@ -1412,15 +1451,13 @@ Tkapp_CallProc(Tkapp_CallEvent *e, int flags)
     i = Tcl_EvalObjv(e->self->interp, objc, objv, e->flags);
     ENTER_PYTHON
     if (i == TCL_ERROR) {
-        *(e->res) = NULL;
-        *(e->exc_type) = NULL;
-        *(e->exc_tb) = NULL;
-        *(e->exc_value) = PyObject_CallFunction(
-            Tkinter_TclError, "s",
-            Tcl_GetStringResult(e->self->interp));
+        *(e->res) = Tkinter_Error(e->self);
     }
     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
 
@@ -1508,9 +1545,9 @@ Tkapp_Call(PyObject *selfptr, PyObject *args)
         ENTER_OVERLAP
 
         if (i == TCL_ERROR)
-            Tkinter_Error(selfptr);
+            Tkinter_Error(self);
         else
-            res = Tkapp_CallResult(self);
+            res = Tkapp_ObjectResult(self);
 
         LEAVE_OVERLAP_TCL
 
@@ -1542,9 +1579,9 @@ _tkinter_tkapp_eval_impl(TkappObject *self, const char *script)
     err = Tcl_Eval(Tkapp_Interp(self), script);
     ENTER_OVERLAP
     if (err == TCL_ERROR)
-        res = Tkinter_Error((PyObject *)self);
+        res = Tkinter_Error(self);
     else
-        res = unicodeFromTclString(Tkapp_Result(self));
+        res = Tkapp_UnicodeResult(self);
     LEAVE_OVERLAP_TCL
     return res;
 }
@@ -1571,9 +1608,9 @@ _tkinter_tkapp_evalfile_impl(TkappObject *self, const char *fileName)
     err = Tcl_EvalFile(Tkapp_Interp(self), fileName);
     ENTER_OVERLAP
     if (err == TCL_ERROR)
-        res = Tkinter_Error((PyObject *)self);
+        res = Tkinter_Error(self);
     else
-        res = unicodeFromTclString(Tkapp_Result(self));
+        res = Tkapp_UnicodeResult(self);
     LEAVE_OVERLAP_TCL
     return res;
 }
@@ -1600,9 +1637,9 @@ _tkinter_tkapp_record_impl(TkappObject *self, const char *script)
     err = Tcl_RecordAndEval(Tkapp_Interp(self), script, TCL_NO_EVAL);
     ENTER_OVERLAP
     if (err == TCL_ERROR)
-        res = Tkinter_Error((PyObject *)self);
+        res = Tkinter_Error(self);
     else
-        res = unicodeFromTclString(Tkapp_Result(self));
+        res = Tkapp_UnicodeResult(self);
     LEAVE_OVERLAP_TCL
     return res;
 }
@@ -1633,13 +1670,13 @@ _tkinter_tkapp_adderrorinfo_impl(TkappObject *self, const char *msg)
 
 /** Tcl Variable **/
 
-typedef PyObject* (*EventFunc)(PyObject*, PyObject *args, int flags);
+typedef PyObject* (*EventFunc)(TkappObject *, PyObject *, int);
 
 TCL_DECLARE_MUTEX(var_mutex)
 
 typedef struct VarEvent {
     Tcl_Event ev; /* must be first */
-    PyObject *self;
+    TkappObject *self;
     PyObject *args;
     int flags;
     EventFunc func;
@@ -1694,7 +1731,7 @@ varname_converter(PyObject *in, void *_out)
         return 1;
     }
     if (PyTclObject_Check(in)) {
-        *out = PyTclObject_TclString(in);
+        *out = Tcl_GetString(((PyTclObject *)in)->value);
         return 1;
     }
     PyErr_Format(PyExc_TypeError,
@@ -1752,7 +1789,7 @@ var_invoke(EventFunc func, PyObject *selfptr, PyObject *args, int flags)
             PyErr_NoMemory();
             return NULL;
         }
-        ev->self = selfptr;
+        ev->self = self;
         ev->args = args;
         ev->flags = flags;
         ev->func = func;
@@ -1772,11 +1809,11 @@ var_invoke(EventFunc func, PyObject *selfptr, PyObject *args, int flags)
         return res;
     }
     /* Tcl is not threaded, or this is the interpreter thread. */
-    return func(selfptr, args, flags);
+    return func(self, args, flags);
 }
 
 static PyObject *
-SetVar(PyObject *self, PyObject *args, int flags)
+SetVar(TkappObject *self, PyObject *args, int flags)
 {
     const char *name1, *name2;
     PyObject *newValue;
@@ -1845,7 +1882,7 @@ Tkapp_GlobalSetVar(PyObject *self, PyObject *args)
 
 
 static PyObject *
-GetVar(PyObject *self, PyObject *args, int flags)
+GetVar(TkappObject *self, PyObject *args, int flags)
 {
     const char *name1, *name2=NULL;
     PyObject *res = NULL;
@@ -1860,10 +1897,9 @@ GetVar(PyObject *self, PyObject *args, int flags)
     tres = Tcl_GetVar2Ex(Tkapp_Interp(self), name1, name2, flags);
     ENTER_OVERLAP
     if (tres == NULL) {
-        PyErr_SetString(Tkinter_TclError,
-                        Tcl_GetStringResult(Tkapp_Interp(self)));
+        Tkinter_Error(self);
     } else {
-        if (((TkappObject*)self)->wantobjects) {
+        if (self->wantobjects) {
             res = FromObj(self, tres);
         }
         else {
@@ -1889,7 +1925,7 @@ Tkapp_GlobalGetVar(PyObject *self, PyObject *args)
 
 
 static PyObject *
-UnsetVar(PyObject *self, PyObject *args, int flags)
+UnsetVar(TkappObject *self, PyObject *args, int flags)
 {
     char *name1, *name2=NULL;
     int code;
@@ -1961,7 +1997,7 @@ _tkinter_tkapp_getint(TkappObject *self, PyObject *arg)
         CHECK_STRING_LENGTH(s);
         value = Tcl_NewStringObj(s, -1);
         if (value == NULL)
-            return Tkinter_Error((PyObject *)self);
+            return Tkinter_Error(self);
     }
     /* 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).
@@ -1970,14 +2006,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).
      */
 #ifdef HAVE_LIBTOMMAMTH
-    result = fromBignumObj((PyObject *)self, value);
+    result = fromBignumObj(self, value);
 #else
-    result = fromWideIntObj((PyObject *)self, value);
+    result = fromWideIntObj(self, value);
 #endif
     Tcl_DecrRefCount(value);
     if (result != NULL || PyErr_Occurred())
         return result;
-    return Tkinter_Error((PyObject *)self);
+    return Tkinter_Error(self);
 }
 
 /*[clinic input]
@@ -2008,7 +2044,7 @@ _tkinter_tkapp_getdouble(TkappObject *self, PyObject *arg)
         if (Tcl_GetDoubleFromObj(Tkapp_Interp(self),
                                  ((PyTclObject*)arg)->value,
                                  &v) == TCL_ERROR)
-            return Tkinter_Error((PyObject *)self);
+            return Tkinter_Error(self);
         return PyFloat_FromDouble(v);
     }
 
@@ -2016,7 +2052,7 @@ _tkinter_tkapp_getdouble(TkappObject *self, PyObject *arg)
         return NULL;
     CHECK_STRING_LENGTH(s);
     if (Tcl_GetDouble(Tkapp_Interp(self), s, &v) == TCL_ERROR)
-        return Tkinter_Error((PyObject *)self);
+        return Tkinter_Error(self);
     return PyFloat_FromDouble(v);
 }
 
@@ -2043,7 +2079,7 @@ _tkinter_tkapp_getboolean(TkappObject *self, PyObject *arg)
         if (Tcl_GetBooleanFromObj(Tkapp_Interp(self),
                                   ((PyTclObject*)arg)->value,
                                   &v) == TCL_ERROR)
-            return Tkinter_Error((PyObject *)self);
+            return Tkinter_Error(self);
         return PyBool_FromLong(v);
     }
 
@@ -2051,7 +2087,7 @@ _tkinter_tkapp_getboolean(TkappObject *self, PyObject *arg)
         return NULL;
     CHECK_STRING_LENGTH(s);
     if (Tcl_GetBoolean(Tkapp_Interp(self), s, &v) == TCL_ERROR)
-        return Tkinter_Error((PyObject *)self);
+        return Tkinter_Error(self);
     return PyBool_FromLong(v);
 }
 
@@ -2077,9 +2113,9 @@ _tkinter_tkapp_exprstring_impl(TkappObject *self, const char *s)
     retval = Tcl_ExprString(Tkapp_Interp(self), s);
     ENTER_OVERLAP
     if (retval == TCL_ERROR)
-        res = Tkinter_Error((PyObject *)self);
+        res = Tkinter_Error(self);
     else
-        res = unicodeFromTclString(Tkapp_Result(self));
+        res = Tkapp_UnicodeResult(self);
     LEAVE_OVERLAP_TCL
     return res;
 }
@@ -2107,7 +2143,7 @@ _tkinter_tkapp_exprlong_impl(TkappObject *self, const char *s)
     retval = Tcl_ExprLong(Tkapp_Interp(self), s, &v);
     ENTER_OVERLAP
     if (retval == TCL_ERROR)
-        res = Tkinter_Error((PyObject *)self);
+        res = Tkinter_Error(self);
     else
         res = PyLong_FromLong(v);
     LEAVE_OVERLAP_TCL
@@ -2138,7 +2174,7 @@ _tkinter_tkapp_exprdouble_impl(TkappObject *self, const char *s)
     ENTER_OVERLAP
     PyFPE_END_PROTECT(retval)
     if (retval == TCL_ERROR)
-        res = Tkinter_Error((PyObject *)self);
+        res = Tkinter_Error(self);
     else
         res = PyFloat_FromDouble(v);
     LEAVE_OVERLAP_TCL
@@ -2167,7 +2203,7 @@ _tkinter_tkapp_exprboolean_impl(TkappObject *self, const char *s)
     retval = Tcl_ExprBoolean(Tkapp_Interp(self), s, &v);
     ENTER_OVERLAP
     if (retval == TCL_ERROR)
-        res = Tkinter_Error((PyObject *)self);
+        res = Tkinter_Error(self);
     else
         res = PyLong_FromLong(v);
     LEAVE_OVERLAP_TCL
@@ -2200,12 +2236,12 @@ _tkinter_tkapp_splitlist(TkappObject *self, PyObject *arg)
         if (Tcl_ListObjGetElements(Tkapp_Interp(self),
                                    ((PyTclObject*)arg)->value,
                                    &objc, &objv) == TCL_ERROR) {
-            return Tkinter_Error((PyObject *)self);
+            return Tkinter_Error(self);
         }
         if (!(v = PyTuple_New(objc)))
             return NULL;
         for (i = 0; i < objc; i++) {
-            PyObject *s = FromObj((PyObject*)self, objv[i]);
+            PyObject *s = FromObj(self, objv[i]);
             if (!s) {
                 Py_DECREF(v);
                 return NULL;
@@ -2233,7 +2269,7 @@ _tkinter_tkapp_splitlist(TkappObject *self, PyObject *arg)
     if (Tcl_SplitList(Tkapp_Interp(self), list,
                       &argc, &argv) == TCL_ERROR)  {
         PyMem_Free(list);
-        return Tkinter_Error((PyObject *)self);
+        return Tkinter_Error(self);
     }
 
     if (!(v = PyTuple_New(argc)))
@@ -2277,16 +2313,16 @@ _tkinter_tkapp_split(TkappObject *self, PyObject *arg)
         int i;
         if (Tcl_ListObjGetElements(Tkapp_Interp(self), value,
                                    &objc, &objv) == TCL_ERROR) {
-            return FromObj((PyObject*)self, value);
+            return FromObj(self, value);
         }
         if (objc == 0)
             return PyUnicode_FromString("");
         if (objc == 1)
-            return FromObj((PyObject*)self, objv[0]);
+            return FromObj(self, objv[0]);
         if (!(v = PyTuple_New(objc)))
             return NULL;
         for (i = 0; i < objc; i++) {
-            PyObject *s = FromObj((PyObject*)self, objv[i]);
+            PyObject *s = FromObj(self, objv[i]);
             if (!s) {
                 Py_DECREF(v);
                 return NULL;
@@ -2333,34 +2369,31 @@ PythonCmd_Error(Tcl_Interp *interp)
  * function or method.
  */
 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;
-    PyObject *func, *arg, *res;
-    int i, rv;
+    PyObject *args, *res;
+    int i;
     Tcl_Obj *obj_res;
 
     ENTER_PYTHON
 
-    /* TBD: no error checking here since we know, via the
-     * Tkapp_CreateCommand() that the client data is a two-tuple
-     */
-    func = data->func;
-
-    /* Create argument list (argv1, ..., argvN) */
-    if (!(arg = PyTuple_New(argc - 1)))
+    /* Create argument tuple (objv1, ..., objvN) */
+    if (!(args = PyTuple_New(objc - 1)))
         return PythonCmd_Error(interp);
 
-    for (i = 0; i < (argc - 1); i++) {
-        PyObject *s = unicodeFromTclString(argv[i + 1]);
+    for (i = 0; i < (objc - 1); i++) {
+        PyObject *s = unicodeFromTclObj(objv[i + 1]);
         if (!s) {
-            Py_DECREF(arg);
+            Py_DECREF(args);
             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)
         return PythonCmd_Error(interp);
@@ -2370,18 +2403,15 @@ PythonCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[
         Py_DECREF(res);
         return PythonCmd_Error(interp);
     }
-    else {
-        Tcl_SetObjResult(interp, obj_res);
-        rv = TCL_OK;
-    }
-
+    Tcl_SetObjResult(interp, obj_res);
     Py_DECREF(res);
 
     LEAVE_PYTHON
 
-    return rv;
+    return TCL_OK;
 }
 
+
 static void
 PythonCmdDelete(ClientData clientData)
 {
@@ -2413,7 +2443,7 @@ static int
 Tkapp_CommandProc(CommandEvent *ev, int flags)
 {
     if (ev->create)
-        *ev->status = Tcl_CreateCommand(
+        *ev->status = Tcl_CreateObjCommand(
             ev->interp, ev->name, PythonCmd,
             ev->data, PythonCmdDelete) == NULL;
     else
@@ -2479,7 +2509,7 @@ _tkinter_tkapp_createcommand_impl(TkappObject *self, const char *name,
     else
     {
         ENTER_TCL
-        err = Tcl_CreateCommand(
+        err = Tcl_CreateObjCommand(
             Tkapp_Interp(self), name, PythonCmd,
             (ClientData)data, PythonCmdDelete) == NULL;
         LEAVE_TCL
@@ -2956,9 +2986,9 @@ _tkinter_tkapp_loadtk_impl(TkappObject *self)
     if (err == TCL_ERROR) {
         /* This sets an exception, but we cannot return right
            away because we need to exit the overlap first. */
-        Tkinter_Error((PyObject *)self);
+        Tkinter_Error(self);
     } else {
-        _tk_exists = Tkapp_Result(self);
+        _tk_exists = Tcl_GetStringResult(Tkapp_Interp(self));
     }
     LEAVE_OVERLAP_TCL
     if (err == TCL_ERROR) {
@@ -2966,8 +2996,7 @@ _tkinter_tkapp_loadtk_impl(TkappObject *self)
     }
     if (_tk_exists == NULL || strcmp(_tk_exists, "1") != 0)     {
         if (Tk_Init(interp)             == TCL_ERROR) {
-            PyErr_SetString(Tkinter_TclError,
-                            Tcl_GetStringResult(Tkapp_Interp(self)));
+            Tkinter_Error(self);
 #ifdef TKINTER_PROTECT_LOADTK
             tk_load_failed = 1;
 #endif



More information about the Python-checkins mailing list