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]

Reply via email to