]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-153333: Read tkinter profile scripts with the source file's encoding (GH-153334)
authortonghuaroot (童话) <tonghuaroot@gmail.com>
Sat, 11 Jul 2026 04:42:35 +0000 (12:42 +0800)
committerGitHub <noreply@github.com>
Sat, 11 Jul 2026 04:42:35 +0000 (04:42 +0000)
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.

Lib/test/test_tkinter/test_misc.py
Lib/tkinter/__init__.py
Misc/NEWS.d/next/Library/2026-07-08-21-15-00.gh-issue-153333.Kp3mZq.rst [new file with mode: 0644]

index e764ce91b1161c4f17e2121c9492949fbc7bf24f..d315a23ef794deabf3b02fa527a4c91d3d6ab29d 100644 (file)
@@ -1,5 +1,6 @@
 import collections.abc
 import functools
+import os
 import gc
 import platform
 import sys
@@ -860,6 +861,26 @@ class MiscTest(AbstractTkTest, unittest.TestCase):
 
 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.
index d8172ff5c4087a34e48af28171767e578bec5bd4..0616dadcbbf210c5a1bb69b5f503d35c31e02084 100644 (file)
@@ -2750,11 +2750,13 @@ class Tk(Misc, Wm):
         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 (file)
index 0000000..6fcbd59
--- /dev/null
@@ -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.