https://github.com/python/cpython/commit/bac73b0f4ea475cbfb11f41579129c9d07075c34
commit: bac73b0f4ea475cbfb11f41579129c9d07075c34
branch: main
author: tonghuaroot (童话) <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-07-11T04:42:35Z
summary:
gh-153333: Read tkinter profile scripts with the source file's encoding
(GH-153334)
Tk.readprofile ran the user's ~/.CLASS.py and ~/.BASE.py scripts with
exec(open(path).read()), decoding them with the locale encoding. Read
them in binary mode so exec() honors each script's own coding cookie.
files:
A Misc/NEWS.d/next/Library/2026-07-08-21-15-00.gh-issue-153333.Kp3mZq.rst
M Lib/test/test_tkinter/test_misc.py
M Lib/tkinter/__init__.py
diff --git a/Lib/test/test_tkinter/test_misc.py
b/Lib/test/test_tkinter/test_misc.py
index e764ce91b1161c..d315a23ef794de 100644
--- a/Lib/test/test_tkinter/test_misc.py
+++ b/Lib/test/test_tkinter/test_misc.py
@@ -1,5 +1,6 @@
import collections.abc
import functools
+import os
import gc
import platform
import sys
@@ -860,6 +861,26 @@ def test_iterable_protocol(self):
class TkTest(AbstractTkTest, unittest.TestCase):
+ def test_readprofile(self):
+ # gh-153333: profile scripts are decoded with their own coding cookie,
+ # not the locale encoding. Two cookies so no locale can mask the bug.
+ profiles = {
+ '.RpClass.py': ('latin-1', "self._rp_latin1 = 'caf\xe9'"),
+ '.rpbase.py': ('utf-8', "self._rp_utf8 = 'caf\xe9'"),
+ }
+ self.addCleanup(self.root.__dict__.pop, '_rp_latin1', None)
+ self.addCleanup(self.root.__dict__.pop, '_rp_utf8', None)
+ with (os_helper.temp_dir() as home,
+ os_helper.EnvironmentVarGuard() as env):
+ env['HOME'] = home
+ for filename, (encoding, body) in profiles.items():
+ script = '# -*- coding: %s -*-\n%s\n' % (encoding, body)
+ with open(os.path.join(home, filename), 'wb') as f:
+ f.write(script.encode(encoding))
+ self.root.readprofile('rpbase', 'RpClass')
+ self.assertEqual(self.root._rp_latin1, 'caf\xe9')
+ self.assertEqual(self.root._rp_utf8, 'caf\xe9')
+
def test_className(self):
# The className argument sets the class of the root window. Tk
# title-cases it: the first letter is upper-cased, the rest
lower-cased.
diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py
index d8172ff5c4087a..0616dadcbbf210 100644
--- a/Lib/tkinter/__init__.py
+++ b/Lib/tkinter/__init__.py
@@ -2750,11 +2750,13 @@ def readprofile(self, baseName, className):
if os.path.isfile(class_tcl):
self.tk.call('source', class_tcl)
if os.path.isfile(class_py):
- exec(open(class_py).read(), dir)
+ with open(class_py, 'rb') as f:
+ exec(f.read(), dir)
if os.path.isfile(base_tcl):
self.tk.call('source', base_tcl)
if os.path.isfile(base_py):
- exec(open(base_py).read(), dir)
+ with open(base_py, 'rb') as f:
+ exec(f.read(), dir)
def report_callback_exception(self, exc, val, tb):
"""Report callback exception on sys.stderr.
diff --git
a/Misc/NEWS.d/next/Library/2026-07-08-21-15-00.gh-issue-153333.Kp3mZq.rst
b/Misc/NEWS.d/next/Library/2026-07-08-21-15-00.gh-issue-153333.Kp3mZq.rst
new file mode 100644
index 00000000000000..6fcbd590d0d9d3
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-08-21-15-00.gh-issue-153333.Kp3mZq.rst
@@ -0,0 +1,3 @@
+The ``readprofile`` method of :class:`tkinter.Tk` now reads the user's
+profile scripts using the encoding declared in the file, instead of the
+locale encoding.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]