Skip to content

Commit bac73b0

Browse files
authored
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.
1 parent c22e9c9 commit bac73b0

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 gc
45
import platform
56
import sys
@@ -860,6 +861,26 @@ def test_iterable_protocol(self):
860861

861862
class TkTest(AbstractTkTest, unittest.TestCase):
862863

864+
def test_readprofile(self):
865+
# gh-153333: profile scripts are decoded with their own coding cookie,
866+
# not the locale encoding. Two cookies so no locale can mask the bug.
867+
profiles = {
868+
'.RpClass.py': ('latin-1', "self._rp_latin1 = 'caf\xe9'"),
869+
'.rpbase.py': ('utf-8', "self._rp_utf8 = 'caf\xe9'"),
870+
}
871+
self.addCleanup(self.root.__dict__.pop, '_rp_latin1', None)
872+
self.addCleanup(self.root.__dict__.pop, '_rp_utf8', None)
873+
with (os_helper.temp_dir() as home,
874+
os_helper.EnvironmentVarGuard() as env):
875+
env['HOME'] = home
876+
for filename, (encoding, body) in profiles.items():
877+
script = '# -*- coding: %s -*-\n%s\n' % (encoding, body)
878+
with open(os.path.join(home, filename), 'wb') as f:
879+
f.write(script.encode(encoding))
880+
self.root.readprofile('rpbase', 'RpClass')
881+
self.assertEqual(self.root._rp_latin1, 'caf\xe9')
882+
self.assertEqual(self.root._rp_utf8, 'caf\xe9')
883+
863884
def test_className(self):
864885
# The className argument sets the class of the root window. Tk
865886
# 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
@@ -2750,11 +2750,13 @@ def readprofile(self, baseName, className):
27502750
if os.path.isfile(class_tcl):
27512751
self.tk.call('source', class_tcl)
27522752
if os.path.isfile(class_py):
2753-
exec(open(class_py).read(), dir)
2753+
with open(class_py, 'rb') as f:
2754+
exec(f.read(), dir)
27542755
if os.path.isfile(base_tcl):
27552756
self.tk.call('source', base_tcl)
27562757
if os.path.isfile(base_py):
2757-
exec(open(base_py).read(), dir)
2758+
with open(base_py, 'rb') as f:
2759+
exec(f.read(), dir)
27582760

27592761
def report_callback_exception(self, exc, val, tb):
27602762
"""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)