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);
}