Skip to content

Commit ae42632

Browse files
[3.15] gh-153333: Read tkinter profile scripts with the source file's encoding (GH-153334) (GH-153541)
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. (cherry picked from commit bac73b0) Co-authored-by: tonghuaroot (童话) <tonghuaroot@gmail.com>
1 parent 3e776ee commit ae42632

3 files changed

Lines changed: 28 additions & 2 deletions

File tree

Lib/test/test_tkinter/test_misc.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import collections.abc
22
import functools
3+
import os
34
import platform
45
import sys
56
import textwrap
@@ -755,6 +756,26 @@ def test_iterable_protocol(self):
755756

756757
class TkTest(AbstractTkTest, unittest.TestCase):
757758

759+
def test_readprofile(self):
760+
# gh-153333: profile scripts are decoded with their own coding cookie,
761+
# not the locale encoding. Two cookies so no locale can mask the bug.
762+
profiles = {
763+
'.RpClass.py': ('latin-1', "self._rp_latin1 = 'caf\xe9'"),
764+
'.rpbase.py': ('utf-8', "self._rp_utf8 = 'caf\xe9'"),
765+
}
766+
self.addCleanup(self.root.__dict__.pop, '_rp_latin1', None)
767+
self.addCleanup(self.root.__dict__.pop, '_rp_utf8', None)
768+
with (os_helper.temp_dir() as home,
769+
os_helper.EnvironmentVarGuard() as env):
770+
env['HOME'] = home
771+
for filename, (encoding, body) in profiles.items():
772+
script = '# -*- coding: %s -*-\n%s\n' % (encoding, body)
773+
with open(os.path.join(home, filename), 'wb') as f:
774+
f.write(script.encode(encoding))
775+
self.root.readprofile('rpbase', 'RpClass')
776+
self.assertEqual(self.root._rp_latin1, 'caf\xe9')
777+
self.assertEqual(self.root._rp_utf8, 'caf\xe9')
778+
758779
def test_className(self):
759780
# The className argument sets the class of the root window. Tk
760781
# title-cases it: the first letter is upper-cased, the rest lower-cased.

Lib/tkinter/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2639,11 +2639,13 @@ def readprofile(self, baseName, className):
26392639
if os.path.isfile(class_tcl):
26402640
self.tk.call('source', class_tcl)
26412641
if os.path.isfile(class_py):
2642-
exec(open(class_py).read(), dir)
2642+
with open(class_py, 'rb') as f:
2643+
exec(f.read(), dir)
26432644
if os.path.isfile(base_tcl):
26442645
self.tk.call('source', base_tcl)
26452646
if os.path.isfile(base_py):
2646-
exec(open(base_py).read(), dir)
2647+
with open(base_py, 'rb') as f:
2648+
exec(f.read(), dir)
26472649

26482650
def report_callback_exception(self, exc, val, tb):
26492651
"""Report callback exception on sys.stderr.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The ``readprofile`` method of :class:`tkinter.Tk` now reads the user's
2+
profile scripts using the encoding declared in the file, instead of the
3+
locale encoding.

0 commit comments

Comments
 (0)