summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2024-06-23 15:33:19 (GMT)
committerGitHub <noreply@github.com>2024-06-23 15:33:19 (GMT)
commit99f18ea68934cb59c89c09e946aa1a504bb33065 (patch)
tree97284930b6b0d020b644866034b3ddefa98c30c9
parent97acd295418493b19d78ed1a26d76e2dc8cc63dd (diff)
downloadcpython-99f18ea68934cb59c89c09e946aa1a504bb33065.zip
cpython-99f18ea68934cb59c89c09e946aa1a504bb33065.tar.gz
cpython-99f18ea68934cb59c89c09e946aa1a504bb33065.tar.bz2
[3.13] gh-101830: Fix Tcl_Obj to string conversion (GH-120884) (GH-120905)
Accessing the Tkinter object's string representation no longer converts the underlying Tcl object to a string on Windows. (cherry picked from commit f4ddaa396715855ffbd94590f89ab7d55feeec07) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
-rw-r--r--Lib/test/test_tcl.py52
-rw-r--r--Misc/NEWS.d/next/Library/2024-06-22-22-23-56.gh-issue-101830.1BAoxH.rst2
-rw-r--r--Modules/_tkinter.c46
3 files changed, 78 insertions, 22 deletions
diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py
index e6cf2c7..443787d 100644
--- a/Lib/test/test_tcl.py
+++ b/Lib/test/test_tcl.py
@@ -51,7 +51,7 @@ class TclTest(unittest.TestCase):
def test_eval_surrogates_in_result(self):
tcl = self.interp
- self.assertIn(tcl.eval(r'set a "<\ud83d\udcbb>"'), '<\U0001f4bb>')
+ self.assertEqual(tcl.eval(r'set a "<\ud83d\udcbb>"'), '<\U0001f4bb>')
def testEvalException(self):
tcl = self.interp
@@ -61,6 +61,13 @@ class TclTest(unittest.TestCase):
tcl = self.interp
self.assertRaises(TclError,tcl.eval,'this is wrong')
+ def test_eval_returns_tcl_obj(self):
+ tcl = self.interp.tk
+ tcl.eval(r'set a "\u20ac \ud83d\udcbb \0 \udcab"; regexp -about $a')
+ a = tcl.eval('set a')
+ expected = '\u20ac \U0001f4bb \0 \udced\udcb2\udcab'
+ self.assertEqual(a, expected)
+
def testCall(self):
tcl = self.interp
tcl.call('set','a','1')
@@ -74,6 +81,18 @@ class TclTest(unittest.TestCase):
tcl = self.interp
self.assertRaises(TclError,tcl.call,'this','is','wrong')
+ def test_call_returns_tcl_obj(self):
+ tcl = self.interp.tk
+ tcl.eval(r'set a "\u20ac \ud83d\udcbb \0 \udcab"; regexp -about $a')
+ a = tcl.call('set', 'a')
+ expected = '\u20ac \U0001f4bb \0 \udced\udcb2\udcab'
+ if self.wantobjects:
+ self.assertEqual(str(a), expected)
+ self.assertEqual(a.string, expected)
+ self.assertEqual(a.typename, 'regexp')
+ else:
+ self.assertEqual(a, expected)
+
def testSetVar(self):
tcl = self.interp
tcl.setvar('a','1')
@@ -102,6 +121,18 @@ class TclTest(unittest.TestCase):
tcl = self.interp
self.assertRaises(TclError,tcl.getvar,'a(1)')
+ def test_getvar_returns_tcl_obj(self):
+ tcl = self.interp.tk
+ tcl.eval(r'set a "\u20ac \ud83d\udcbb \0 \udcab"; regexp -about $a')
+ a = tcl.getvar('a')
+ expected = '\u20ac \U0001f4bb \0 \udced\udcb2\udcab'
+ if self.wantobjects:
+ self.assertEqual(str(a), expected)
+ self.assertEqual(a.string, expected)
+ self.assertEqual(a.typename, 'regexp')
+ else:
+ self.assertEqual(a, expected)
+
def testUnsetVar(self):
tcl = self.interp
tcl.setvar('a',1)
@@ -549,6 +580,24 @@ class TclTest(unittest.TestCase):
'1 2 {3 4} {5 6} {}',
(1, (2,), (3, 4), '5 6', ''))
+ def test_passing_tcl_obj(self):
+ tcl = self.interp.tk
+ a = None
+ def testfunc(arg):
+ nonlocal a
+ a = arg
+ self.interp.createcommand('testfunc', testfunc)
+ self.addCleanup(self.interp.tk.deletecommand, 'testfunc')
+ tcl.eval(r'set a "\u20ac \ud83d\udcbb \0 \udcab"; regexp -about $a')
+ tcl.eval(r'testfunc $a')
+ expected = '\u20ac \U0001f4bb \0 \udced\udcb2\udcab'
+ if self.wantobjects >= 2:
+ self.assertEqual(str(a), expected)
+ self.assertEqual(a.string, expected)
+ self.assertEqual(a.typename, 'regexp')
+ else:
+ self.assertEqual(a, expected)
+
def test_splitlist(self):
splitlist = self.interp.tk.splitlist
call = self.interp.tk.call
@@ -673,6 +722,7 @@ class TclTest(unittest.TestCase):
support.check_disallow_instantiation(self, _tkinter.TkttType)
support.check_disallow_instantiation(self, _tkinter.TkappType)
+
class BigmemTclTest(unittest.TestCase):
def setUp(self):
diff --git a/Misc/NEWS.d/next/Library/2024-06-22-22-23-56.gh-issue-101830.1BAoxH.rst b/Misc/NEWS.d/next/Library/2024-06-22-22-23-56.gh-issue-101830.1BAoxH.rst
new file mode 100644
index 0000000..46c18b0
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-06-22-22-23-56.gh-issue-101830.1BAoxH.rst
@@ -0,0 +1,2 @@
+Accessing the :mod:`tkinter` object's string representation no longer converts
+the underlying Tcl object to a string on Windows.
diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c
index 24f87c8..8fe2c5b 100644
--- a/Modules/_tkinter.c
+++ b/Modules/_tkinter.c
@@ -493,24 +493,28 @@ unicodeFromTclString(const char *s)
}
static PyObject *
-unicodeFromTclObj(Tcl_Obj *value)
+unicodeFromTclObj(TkappObject *tkapp, Tcl_Obj *value)
{
Tcl_Size 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
+ if (value->typePtr != NULL && tkapp != NULL &&
+ (value->typePtr == tkapp->StringType ||
+ value->typePtr == tkapp->UTF32StringType))
+ {
+ 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();
+ }
+#endif
const char *s = Tcl_GetStringFromObj(value, &len);
return unicodeFromTclStringAndSize(s, len);
-#endif
}
/*[clinic input]
@@ -793,7 +797,7 @@ PyTclObject_string(PyObject *_self, void *ignored)
{
PyTclObject *self = (PyTclObject *)_self;
if (!self->string) {
- self->string = unicodeFromTclObj(self->value);
+ self->string = unicodeFromTclObj(NULL, self->value);
if (!self->string)
return NULL;
}
@@ -808,7 +812,7 @@ PyTclObject_str(PyObject *_self)
return Py_NewRef(self->string);
}
/* XXX Could cache result if it is non-ASCII. */
- return unicodeFromTclObj(self->value);
+ return unicodeFromTclObj(NULL, self->value);
}
static PyObject *
@@ -1143,7 +1147,7 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value)
Tcl_Interp *interp = Tkapp_Interp(tkapp);
if (value->typePtr == NULL) {
- return unicodeFromTclObj(value);
+ return unicodeFromTclObj(tkapp, value);
}
if (value->typePtr == tkapp->BooleanType ||
@@ -1208,7 +1212,7 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value)
if (value->typePtr == tkapp->StringType ||
value->typePtr == tkapp->UTF32StringType)
{
- return unicodeFromTclObj(value);
+ return unicodeFromTclObj(tkapp, value);
}
if (tkapp->BignumType == NULL &&
@@ -1308,7 +1312,7 @@ finally:
static PyObject *
Tkapp_UnicodeResult(TkappObject *self)
{
- return unicodeFromTclObj(Tcl_GetObjResult(self->interp));
+ return unicodeFromTclObj(self, Tcl_GetObjResult(self->interp));
}
@@ -1327,7 +1331,7 @@ Tkapp_ObjectResult(TkappObject *self)
res = FromObj(self, value);
Tcl_DecrRefCount(value);
} else {
- res = unicodeFromTclObj(value);
+ res = unicodeFromTclObj(self, value);
}
return res;
}
@@ -1859,7 +1863,7 @@ GetVar(TkappObject *self, PyObject *args, int flags)
res = FromObj(self, tres);
}
else {
- res = unicodeFromTclObj(tres);
+ res = unicodeFromTclObj(self, tres);
}
}
LEAVE_OVERLAP_TCL
@@ -2306,7 +2310,7 @@ PythonCmd(ClientData clientData, Tcl_Interp *interp,
for (i = 0; i < (objc - 1); i++) {
PyObject *s = objargs ? FromObj(data->self, objv[i + 1])
- : unicodeFromTclObj(objv[i + 1]);
+ : unicodeFromTclObj(data->self, objv[i + 1]);
if (!s) {
Py_DECREF(args);
return PythonCmd_Error(interp);