https://github.com/python/cpython/commit/53ca52ddb0c2725a1c34e4eb2245339cd3dcaa84
commit: 53ca52ddb0c2725a1c34e4eb2245339cd3dcaa84
branch: main
author: Terry Jan Reedy <[email protected]>
committer: terryjreedy <[email protected]>
date: 2026-07-01T04:58:39Z
summary:
gh-152728: IDLE - move 3 toplevel fix_xyz functions to idlelb.util (#152729)
IDLE - move 3 toplevel fix_xyz functions to idlelb.util
Move idlelib functions run.fix_scaling, editor.fixwordbreaks
(as fix_word_breaks). All are used in at least 3 modules.
files:
A Misc/NEWS.d/next/IDLE/2026-07-01-00-15-58.gh-issue-152728.yxIhMN.rst
M Lib/idlelib/News3.txt
M Lib/idlelib/editor.py
M Lib/idlelib/filelist.py
M Lib/idlelib/idle_test/test_editmenu.py
M Lib/idlelib/idle_test/test_sidebar.py
M Lib/idlelib/pyshell.py
M Lib/idlelib/run.py
M Lib/idlelib/util.py
diff --git a/Lib/idlelib/News3.txt b/Lib/idlelib/News3.txt
index 97becb858fea333..dc18b6e07eb364b 100644
--- a/Lib/idlelib/News3.txt
+++ b/Lib/idlelib/News3.txt
@@ -4,6 +4,9 @@ Released on 2026-10-01
=========================
+gh-152728: Move functions run.fix_scaling, editor.fixwordbreaks (as
fix_word_breaks)
+and pyshell.fix_x11_paste to module util. Patch by Terry J. Reedy.
+
gh-85320: IDLE now reads and writes its configuration files and the
breakpoints file using UTF-8 instead of the locale encoding.
Files with non-ASCII characters and non-UTF-8 encoding may need
diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py
index 239bf5af4705674..a040d791bdeb528 100644
--- a/Lib/idlelib/editor.py
+++ b/Lib/idlelib/editor.py
@@ -1704,19 +1704,10 @@ def get_accelerator(keydefs, eventname):
return s
-def fixwordbreaks(root):
- # On Windows, tcl/tk breaks 'words' only on spaces, as in Command Prompt.
- # We want Motif style everywhere. See #21474, msg218992 and followup.
- tk = root.tk
- tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
- tk.call('set', 'tcl_wordchars', r'\w')
- tk.call('set', 'tcl_nonwordchars', r'\W')
-
-
-def _editor_window(parent): # htest #
- # error if close master window first - timer event, after script
- root = parent
- fixwordbreaks(root)
+def _editor_window(root): # htest #
+ # Error if close master window first - timer event, after script
+ from util import fix_word_breaks
+ fix_word_breaks(root)
if sys.argv[1:]:
filename = sys.argv[1]
else:
diff --git a/Lib/idlelib/filelist.py b/Lib/idlelib/filelist.py
index e27e5d32a0ff633..384908f911f5153 100644
--- a/Lib/idlelib/filelist.py
+++ b/Lib/idlelib/filelist.py
@@ -112,12 +112,12 @@ def canonize(self, filename):
def _test(): # TODO check and convert to htest
+ # Maybe redundant with test_filelist.FileListTest.test_new_empty.
from tkinter import Tk
- from idlelib.editor import fixwordbreaks
- from idlelib.run import fix_scaling
+ from idlelib.util import fix_scaling, fix_word_breaks
root = Tk()
fix_scaling(root)
- fixwordbreaks(root)
+ fix_word_breaks(root)
root.withdraw()
flist = FileList(root)
flist.new()
diff --git a/Lib/idlelib/idle_test/test_editmenu.py
b/Lib/idlelib/idle_test/test_editmenu.py
index 17478473a3d1b27..0d1eb9054918da7 100644
--- a/Lib/idlelib/idle_test/test_editmenu.py
+++ b/Lib/idlelib/idle_test/test_editmenu.py
@@ -7,7 +7,7 @@
import tkinter as tk
from tkinter import ttk
import unittest
-from idlelib import pyshell
+from idlelib.util import fix_x11_paste
class PasteTest(unittest.TestCase):
'''Test pasting into widgets that allow pasting.
@@ -18,7 +18,7 @@ class PasteTest(unittest.TestCase):
def setUpClass(cls):
cls.root = root = tk.Tk()
cls.root.withdraw()
- pyshell.fix_x11_paste(root)
+ fix_x11_paste(root)
cls.text = tk.Text(root)
cls.entry = tk.Entry(root)
cls.tentry = ttk.Entry(root)
diff --git a/Lib/idlelib/idle_test/test_sidebar.py
b/Lib/idlelib/idle_test/test_sidebar.py
index 4157a4b4dcdd2aa..dc3431d8d8ef887 100644
--- a/Lib/idlelib/idle_test/test_sidebar.py
+++ b/Lib/idlelib/idle_test/test_sidebar.py
@@ -11,11 +11,10 @@
from idlelib.idle_test.tkinter_testing_utils import run_in_tk_mainloop
from idlelib.delegator import Delegator
-from idlelib.editor import fixwordbreaks
from idlelib.percolator import Percolator
import idlelib.pyshell
-from idlelib.pyshell import fix_x11_paste, PyShell, PyShellFileList
-from idlelib.run import fix_scaling
+from idlelib.pyshell import PyShell, PyShellFileList
+from idlelib.util import fix_scaling, fix_word_breaks, fix_x11_paste
import idlelib.sidebar
from idlelib.sidebar import get_end_linenumber, get_lineno
@@ -403,7 +402,7 @@ def setUpClass(cls):
root.withdraw()
fix_scaling(root)
- fixwordbreaks(root)
+ fix_word_breaks(root)
fix_x11_paste(root)
cls.flist = flist = PyShellFileList(root)
diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py
index b1662491935e4a0..439f98170292b07 100755
--- a/Lib/idlelib/pyshell.py
+++ b/Lib/idlelib/pyshell.py
@@ -36,13 +36,14 @@
from idlelib.delegator import Delegator
from idlelib import debugger
from idlelib import debugger_r
-from idlelib.editor import EditorWindow, fixwordbreaks
+from idlelib.editor import EditorWindow
from idlelib.filelist import FileList
from idlelib.outwin import OutputWindow
from idlelib import replace
from idlelib import rpc
from idlelib.run import idle_formatwarning, StdInputFile, StdOutputFile
from idlelib.undo import UndoDelegator
+from idlelib.util import fix_word_breaks
# Default for testing; defaults to True in main() for running.
use_subprocess = False
@@ -881,9 +882,9 @@ def __init__(self, flist=None):
if ms[2][0] != "shell":
ms.insert(2, ("shell", "She_ll"))
self.interp = ModifiedInterpreter(self)
- if flist is None:
+ if flist is None: # TODO possible? root and flist in main.
root = Tk()
- fixwordbreaks(root)
+ fix_word_breaks(root)
root.withdraw()
flist = PyShellFileList(root)
@@ -1452,17 +1453,6 @@ def on_squeezed_expand(self, index, text, tags):
self.shell_sidebar.update_sidebar()
-def fix_x11_paste(root):
- "Make paste replace selection on x11. See issue #5124."
- if root._windowingsystem == 'x11':
- for cls in 'Text', 'Entry', 'Spinbox':
- root.bind_class(
- cls,
- '<<Paste>>',
- 'catch {%W delete sel.first sel.last}\n' +
- root.bind_class(cls, '<<Paste>>'))
-
-
usage_msg = """\
USAGE: idle [-deins] [-t title] [file]*
@@ -1522,6 +1512,7 @@ def main():
from platform import system
from idlelib import testing # bool value
from idlelib import macosx
+ from idlelib.util import fix_scaling, fix_x11_paste
global flist, root, use_subprocess
@@ -1607,7 +1598,6 @@ def main():
NoDefaultRoot()
root = Tk(className="Idle")
root.withdraw()
- from idlelib.run import fix_scaling
fix_scaling(root)
# set application icon
@@ -1629,7 +1619,7 @@ def main():
root.wm_iconphoto(True, *icons)
# start editor and/or shell windows:
- fixwordbreaks(root)
+ fix_word_breaks(root)
fix_x11_paste(root)
flist = PyShellFileList(root)
macosx.setupApp(root, flist)
diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py
index e1c40fee8f48053..f5564ccf90f7f46 100644
--- a/Lib/idlelib/run.py
+++ b/Lib/idlelib/run.py
@@ -25,6 +25,7 @@
from idlelib import iomenu # encoding
from idlelib import rpc # multiple objects
from idlelib import stackviewer # StackTreeItem
+from idlelib import util # fix_scaling
import __main__
import tkinter # Use tcl and, if startup fails, messagebox.
@@ -216,7 +217,7 @@ def show_socket_error(err, address):
import tkinter
from tkinter.messagebox import showerror
root = tkinter.Tk()
- fix_scaling(root)
+ util.fix_scaling(root)
root.withdraw()
showerror(
"Subprocess Connection Error",
@@ -389,16 +390,6 @@ def exit():
sys.exit(0)
-def fix_scaling(root):
- """Scale fonts on HiDPI displays."""
- import tkinter.font
- scaling = float(root.tk.call('tk', 'scaling'))
- if scaling > 1.4:
- for name in tkinter.font.names(root):
- font = tkinter.font.Font(root=root, name=name, exists=True)
- size = int(font['size'])
- if size < 0:
- font['size'] = round(-0.75*size)
def fixdoc(fun, text):
diff --git a/Lib/idlelib/util.py b/Lib/idlelib/util.py
index e05604ab4853f6d..bf88c905e1d177d 100644
--- a/Lib/idlelib/util.py
+++ b/Lib/idlelib/util.py
@@ -19,6 +19,20 @@
py_extensions = ('.py', '.pyw', '.pyi')
+# fix_x functions seem only needed once per process.
+
+def fix_scaling(root): # Called in filelist _test, pyshell, and run.
+ """Scale fonts on HiDPI displays, once per process."""
+ import tkinter.font
+ scaling = float(root.tk.call('tk', 'scaling'))
+ if scaling > 1.4:
+ for name in tkinter.font.names(root):
+ font = tkinter.font.Font(root=root, name=name, exists=True)
+ size = int(font['size'])
+ if size < 0:
+ font['size'] = round(-0.75*size)
+
+
# Fix for HiDPI screens on Windows. CALL BEFORE ANY TK OPERATIONS!
# URL for arguments for the ...Awareness call below.
#
https://msdn.microsoft.com/en-us/library/windows/desktop/dn280512(v=vs.85).aspx
@@ -31,6 +45,25 @@ def fix_win_hidpi(): # Called in pyshell and turtledemo.
except (ImportError, AttributeError, OSError):
pass
+def fix_word_breaks(root): # Called in editor htest, filelist _test, pyshell.
+ # On Windows, tcl/tk breaks 'words' only on spaces, as in Command Prompt.
+ # We want Motif style everywhere. See #21474, msg218992 and followup.
+ tk = root.tk
+ tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
+ tk.call('set', 'tcl_wordchars', r'\w')
+ tk.call('set', 'tcl_nonwordchars', r'\W')
+
+
+def fix_x11_paste(root):
+ "Make paste replace selection on x11. See issue #5124."
+ if root._windowingsystem == 'x11':
+ for cls in 'Text', 'Entry', 'Spinbox':
+ root.bind_class(
+ cls,
+ '<<Paste>>',
+ 'catch {%W delete sel.first sel.last}\n' +
+ root.bind_class(cls, '<<Paste>>'))
+
if __name__ == '__main__':
from unittest import main
diff --git
a/Misc/NEWS.d/next/IDLE/2026-07-01-00-15-58.gh-issue-152728.yxIhMN.rst
b/Misc/NEWS.d/next/IDLE/2026-07-01-00-15-58.gh-issue-152728.yxIhMN.rst
new file mode 100644
index 000000000000000..d73512a615c1898
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2026-07-01-00-15-58.gh-issue-152728.yxIhMN.rst
@@ -0,0 +1,2 @@
+Move functions run.fix_scaling, editor.fixwordbreaks (as fix_word_breaks)
+and pyshell.fix_x11_paste to idlelib.util.
_______________________________________________
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]