diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py index 698ba818029fe8..285a5d0938d2de 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 platform import sys import textwrap @@ -755,6 +756,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 7189471ee3fa0e..3b693bc72f5723 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -2639,11 +2639,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.