diff -r 0a306e5ce5aa Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py Sat Jan 04 22:49:40 2014 +0200 +++ b/Lib/test/test_tcl.py Sun Jan 05 13:11:47 2014 +0200 @@ -171,6 +171,7 @@ self.assertEqual(passValue(True), True if self.wantobjects else '1') self.assertEqual(passValue(False), False if self.wantobjects else '0') self.assertEqual(passValue('string'), 'string') + self.assertEqual(passValue('string\xbd'), 'string\xbd') self.assertEqual(passValue('string\u20ac'), 'string\u20ac') for i in (0, 1, -1, 2**31-1, -2**31): self.assertEqual(passValue(i), i if self.wantobjects else str(i)) @@ -194,6 +195,40 @@ self.assertEqual(passValue((1, '2', (3.4,))), (1, '2', (3.4,)) if self.wantobjects else '1 2 3.4') + def test_user_command(self): + result = None + def testfunc(arg): + nonlocal result + result = arg + self.interp.createcommand('testfunc', testfunc) + + def check(value, expected): + self.interp.call('testfunc', value) + self.assertEqual(result, expected) + + check(True, '1') + check(False, '0') + check('string', 'string') + check('string\xbd', 'string\xbd') + check('string\u20ac', 'string\u20ac') + check('string\udca2', 'string\ufffd\ufffd\ufffd') + check('string\ud801\udca2', + 'string\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd') + check('str\x00ing', 'str\x00ing') + check('str\x00ing\xbd', 'str\x00ing\xbd') + check('str\x00ing\u20ac', 'str\x00ing\u20ac') + for i in (0, 1, -1, 2**31-1, -2**31): + check(i, str(i)) + for f in (0.0, 1.0, -1.0, 1/3, + sys.float_info.min, sys.float_info.max, + -sys.float_info.min, -sys.float_info.max): + check(f, str(f)) + check(float('nan'), 'NaN') + check(float('inf'), 'Inf') + check(-float('inf'), '-Inf') + check((), '') + check((1, '2', (3.4,)), '1 2 3.4') + def test_splitlist(self): splitlist = self.interp.tk.splitlist call = self.interp.tk.call diff -r 0a306e5ce5aa Modules/_tkinter.c --- a/Modules/_tkinter.c Sat Jan 04 22:49:40 2014 +0200 +++ b/Modules/_tkinter.c Sun Jan 05 13:11:47 2014 +0200 @@ -343,6 +343,44 @@ static PyObject * +fromTclStringAndSize(const char *s, Py_ssize_t size) +{ + PyObject *r = PyUnicode_DecodeUTF8(s, size, NULL); + if (!r && PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) { + char *buf = NULL; + PyErr_Clear(); + /* Tcl encodes null character as \xc0\x80 */ + if (memchr(s, '\xc0', size)) { + const char *e = s + size; + char *q = buf = (char *)PyMem_Malloc(size); + if (buf == NULL) + return NULL; + 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, "replace"); + if (buf != NULL) + PyMem_Free(buf); + } + return r; +} + +static PyObject * +fromTclString(const char *s) +{ + return fromTclStringAndSize(s, strlen(s)); +} + + +static PyObject * Split(char *list) { int argc; @@ -1899,20 +1937,8 @@ return PythonCmd_Error(interp); for (i = 0; i < (argc - 1); i++) { - PyObject *s = PyUnicode_FromString(argv[i + 1]); - if (!s) { - /* Is Tk leaking 0xC080 in %A - a "modified" utf-8 null? */ - if (PyErr_ExceptionMatches(PyExc_UnicodeDecodeError) && - !strcmp(argv[i + 1], "\xC0\x80")) { - PyErr_Clear(); - /* Convert to "strict" utf-8 null */ - s = PyUnicode_FromString("\0"); - } else { - Py_DECREF(arg); - return PythonCmd_Error(interp); - } - } - if (PyTuple_SetItem(arg, i, s)) { + PyObject *s = fromTclString(argv[i + 1]); + if (!s || PyTuple_SetItem(arg, i, s)) { Py_DECREF(arg); return PythonCmd_Error(interp); }