[Python-checkins] bpo-5680: IDLE: Customize running a module (GH-13763)

Terry Jan Reedy webhook-mailer at python.org
Mon Jun 17 22:24:27 EDT 2019


https://github.com/python/cpython/commit/201bc2d18b60adb05810d2a6ab396047bc527088
commit: 201bc2d18b60adb05810d2a6ab396047bc527088
branch: master
author: Cheryl Sabella <cheryl.sabella at gmail.com>
committer: Terry Jan Reedy <tjreedy at udel.edu>
date: 2019-06-17T22:24:10-04:00
summary:

bpo-5680: IDLE: Customize running a module (GH-13763)

The initialize options are 1) add command line options, which are appended to sys.argv as if passed on a real command line, and 2) skip the shell restart. The customization dialog is accessed by a new entry on the Run menu.

files:
A Misc/NEWS.d/next/IDLE/2019-06-03-00-39-29.bpo-5680.VCQfOO.rst
M Doc/library/idle.rst
M Lib/idlelib/config-keys.def
M Lib/idlelib/config.py
M Lib/idlelib/editor.py
M Lib/idlelib/help.html
M Lib/idlelib/idle_test/htest.py
M Lib/idlelib/idle_test/test_query.py
M Lib/idlelib/mainmenu.py
M Lib/idlelib/query.py
M Lib/idlelib/runscript.py

diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst
index d494c9766eb7..fb886a714d74 100644
--- a/Doc/library/idle.rst
+++ b/Doc/library/idle.rst
@@ -207,9 +207,13 @@ Strip trailing whitespace
 Run menu (Editor window only)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+.. _python-shell:
+
 Python Shell
    Open or wake up the Python Shell window.
 
+.. _check-module:
+
 Check Module
    Check the syntax of the module currently open in the Editor window. If the
    module has not been saved IDLE will either prompt the user to save or
@@ -217,8 +221,10 @@ Check Module
    there is a syntax error, the approximate location is indicated in the
    Editor window.
 
+.. _run-module:
+
 Run Module
-   Do Check Module (above).  If no error, restart the shell to clean the
+   Do :ref:`Check Module <check-module>`.  If no error, restart the shell to clean the
    environment, then execute the module.  Output is displayed in the Shell
    window.  Note that output requires use of ``print`` or ``write``.
    When execution is complete, the Shell retains focus and displays a prompt.
@@ -226,6 +232,14 @@ Run Module
    This is similar to executing a file with ``python -i file`` at a command
    line.
 
+.. _run-custom:
+
+Run... Customized
+   Same as :ref:`Run Module <run-module>`, but run the module with customized
+   settings.  *Command Line Arguments* extend :data:`sys.argv` as if passed
+   on a command line. The module can be run in the Shell without restarting.
+
+
 Shell menu (Shell window only)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def
index fd235194dfc9..f71269b5b49f 100644
--- a/Lib/idlelib/config-keys.def
+++ b/Lib/idlelib/config-keys.def
@@ -63,6 +63,7 @@ force-open-calltip= <Control-Key-backslash>
 format-paragraph= <Alt-Key-q>
 flash-paren= <Control-Key-0>
 run-module= <Key-F5>
+run-custom= <Shift-Key-F5>
 check-module= <Alt-Key-x>
 zoom-height= <Alt-Key-2>
 
@@ -122,6 +123,7 @@ force-open-calltip= <Control-Key-backslash>
 format-paragraph= <Alt-Key-q>
 flash-paren= <Control-Key-0>
 run-module= <Key-F5>
+run-custom= <Shift-Key-F5>
 check-module= <Alt-Key-x>
 zoom-height= <Alt-Key-2>
 
@@ -181,6 +183,7 @@ force-open-calltip= <Control-Key-backslash>
 format-paragraph= <Alt-Key-q>
 flash-paren= <Control-Key-0>
 run-module= <Key-F5>
+run-custom= <Shift-Key-F5>
 check-module= <Alt-Key-x>
 zoom-height= <Alt-Key-2>
 
@@ -240,6 +243,7 @@ force-open-calltip= <Control-Key-backslash>
 format-paragraph= <Option-Key-q>
 flash-paren= <Control-Key-0>
 run-module= <Key-F5>
+run-custom= <Shift-Key-F5>
 check-module= <Option-Key-x>
 zoom-height= <Option-Key-0>
 
@@ -300,5 +304,6 @@ force-open-calltip= <Control-Key-backslash>
 format-paragraph= <Option-Key-q>
 flash-paren= <Control-Key-0>
 run-module= <Key-F5>
+run-custom= <Shift-Key-F5>
 check-module= <Option-Key-x>
 zoom-height= <Option-Key-0>
diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py
index 12113c19c086..2233dacd66ba 100644
--- a/Lib/idlelib/config.py
+++ b/Lib/idlelib/config.py
@@ -591,7 +591,9 @@ def IsCoreBinding(self, virtualEvent):
     former_extension_events = {  #  Those with user-configurable keys.
         '<<force-open-completions>>', '<<expand-word>>',
         '<<force-open-calltip>>', '<<flash-paren>>', '<<format-paragraph>>',
-         '<<run-module>>', '<<check-module>>', '<<zoom-height>>'}
+         '<<run-module>>', '<<check-module>>', '<<zoom-height>>',
+         '<<run-custom>>',
+         }
 
     def GetCoreKeys(self, keySetName=None):
         """Return dict of core virtual-key keybindings for keySetName.
@@ -658,6 +660,7 @@ def GetCoreKeys(self, keySetName=None):
             '<<flash-paren>>': ['<Control-Key-0>'],
             '<<format-paragraph>>': ['<Alt-Key-q>'],
             '<<run-module>>': ['<Key-F5>'],
+            '<<run-custom>>': ['<Shift-Key-F5>'],
             '<<check-module>>': ['<Alt-Key-x>'],
             '<<zoom-height>>': ['<Alt-Key-2>'],
             }
diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py
index a6674728cd93..606de71a6add 100644
--- a/Lib/idlelib/editor.py
+++ b/Lib/idlelib/editor.py
@@ -304,6 +304,7 @@ def __init__(self, flist=None, filename=None, key=None, root=None):
         scriptbinding = ScriptBinding(self)
         text.bind("<<check-module>>", scriptbinding.check_module_event)
         text.bind("<<run-module>>", scriptbinding.run_module_event)
+        text.bind("<<run-custom>>", scriptbinding.run_custom_event)
         text.bind("<<do-rstrip>>", self.Rstrip(self).do_rstrip)
         ctip = self.Calltip(self)
         text.bind("<<try-open-calltip>>", ctip.try_open_calltip_event)
diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html
index e27ec8d6e173..91803fd06c8f 100644
--- a/Lib/idlelib/help.html
+++ b/Lib/idlelib/help.html
@@ -248,17 +248,21 @@ <h3>Edit menu (Shell and Editor)<a class="headerlink" href="#edit-menu-shell-and
 </div>
 <div class="section" id="run-menu-editor-window-only">
 <span id="index-2"></span><h3>Run menu (Editor window only)<a class="headerlink" href="#run-menu-editor-window-only" title="Permalink to this headline">¶</a></h3>
-<dl class="docutils">
+<dl class="docutils" id="python-shell">
 <dt>Python Shell</dt>
 <dd>Open or wake up the Python Shell window.</dd>
+</dl>
+<dl class="docutils" id="check-module">
 <dt>Check Module</dt>
 <dd>Check the syntax of the module currently open in the Editor window. If the
 module has not been saved IDLE will either prompt the user to save or
 autosave, as selected in the General tab of the Idle Settings dialog.  If
 there is a syntax error, the approximate location is indicated in the
 Editor window.</dd>
+</dl>
+<dl class="docutils" id="run-module">
 <dt>Run Module</dt>
-<dd>Do Check Module (above).  If no error, restart the shell to clean the
+<dd>Do <a class="reference internal" href="#check-module"><span class="std std-ref">Check Module</span></a>.  If no error, restart the shell to clean the
 environment, then execute the module.  Output is displayed in the Shell
 window.  Note that output requires use of <code class="docutils literal notranslate"><span class="pre">print</span></code> or <code class="docutils literal notranslate"><span class="pre">write</span></code>.
 When execution is complete, the Shell retains focus and displays a prompt.
@@ -266,6 +270,12 @@ <h3>Edit menu (Shell and Editor)<a class="headerlink" href="#edit-menu-shell-and
 This is similar to executing a file with <code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">-i</span> <span class="pre">file</span></code> at a command
 line.</dd>
 </dl>
+<dl class="docutils" id="run-custom">
+<dt>Run… Customized</dt>
+<dd>Same as <a class="reference internal" href="#run-module"><span class="std std-ref">Run Module</span></a>, but run the module with customized
+settings.  <em>Command Line Arguments</em> extend <a class="reference internal" href="sys.html#sys.argv" title="sys.argv"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.argv</span></code></a> as if passed
+on a command line. The module can be run in the Shell without restarting.</dd>
+</dl>
 </div>
 <div class="section" id="shell-menu-shell-window-only">
 <h3>Shell menu (Shell window only)<a class="headerlink" href="#shell-menu-shell-window-only" title="Permalink to this headline">¶</a></h3>
diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py
index 6e3398ed0bc8..6ce8cc8a5f60 100644
--- a/Lib/idlelib/idle_test/htest.py
+++ b/Lib/idlelib/idle_test/htest.py
@@ -108,6 +108,15 @@ def _wrapper(parent):  # htest #
            "The default color scheme is in idlelib/config-highlight.def"
     }
 
+CustomRun_spec = {
+    'file': 'query',
+    'kwds': {'title': 'Custom Run Args',
+             '_htest': True},
+    'msg': "Enter with <Return> or [Ok].  Print valid entry to Shell\n"
+           "Arguments are parsed into a list\n"
+           "Close dialog with valid entry, <Escape>, [Cancel], [X]"
+    }
+
 ConfigDialog_spec = {
     'file': 'configdialog',
     'kwds': {'title': 'ConfigDialogTest',
diff --git a/Lib/idlelib/idle_test/test_query.py b/Lib/idlelib/idle_test/test_query.py
index c1c4a25cc506..3b444de15d7c 100644
--- a/Lib/idlelib/idle_test/test_query.py
+++ b/Lib/idlelib/idle_test/test_query.py
@@ -1,4 +1,4 @@
-"""Test query, coverage 91%).
+"""Test query, coverage 93%).
 
 Non-gui tests for Query, SectionName, ModuleName, and HelpSource use
 dummy versions that extract the non-gui methods and add other needed
@@ -30,11 +30,9 @@ class Dummy_Query:
         ok = query.Query.ok
         cancel = query.Query.cancel
         # Add attributes and initialization needed for tests.
-        entry = Var()
-        entry_error = {}
         def __init__(self, dummy_entry):
-            self.entry.set(dummy_entry)
-            self.entry_error['text'] = ''
+            self.entry = Var(value=dummy_entry)
+            self.entry_error = {'text': ''}
             self.result = None
             self.destroyed = False
         def showerror(self, message):
@@ -80,11 +78,9 @@ class SectionNameTest(unittest.TestCase):
     class Dummy_SectionName:
         entry_ok = query.SectionName.entry_ok  # Function being tested.
         used_names = ['used']
-        entry = Var()
-        entry_error = {}
         def __init__(self, dummy_entry):
-            self.entry.set(dummy_entry)
-            self.entry_error['text'] = ''
+            self.entry = Var(value=dummy_entry)
+            self.entry_error = {'text': ''}
         def showerror(self, message):
             self.entry_error['text'] = message
 
@@ -115,11 +111,9 @@ class ModuleNameTest(unittest.TestCase):
     class Dummy_ModuleName:
         entry_ok = query.ModuleName.entry_ok  # Function being tested.
         text0 = ''
-        entry = Var()
-        entry_error = {}
         def __init__(self, dummy_entry):
-            self.entry.set(dummy_entry)
-            self.entry_error['text'] = ''
+            self.entry = Var(value=dummy_entry)
+            self.entry_error = {'text': ''}
         def showerror(self, message):
             self.entry_error['text'] = message
 
@@ -144,9 +138,7 @@ def test_good_module_name(self):
         self.assertEqual(dialog.entry_error['text'], '')
 
 
-# 3 HelpSource test classes each test one function.
-
-orig_platform = query.platform
+# 3 HelpSource test classes each test one method.
 
 class HelpsourceBrowsefileTest(unittest.TestCase):
     "Test browse_file method of ModuleName subclass of Query."
@@ -178,17 +170,16 @@ class HelpsourcePathokTest(unittest.TestCase):
 
     class Dummy_HelpSource:
         path_ok = query.HelpSource.path_ok
-        path = Var()
-        path_error = {}
         def __init__(self, dummy_path):
-            self.path.set(dummy_path)
-            self.path_error['text'] = ''
+            self.path = Var(value=dummy_path)
+            self.path_error = {'text': ''}
         def showerror(self, message, widget=None):
             self.path_error['text'] = message
 
+    orig_platform = query.platform  # Set in test_path_ok_file.
     @classmethod
     def tearDownClass(cls):
-        query.platform = orig_platform
+        query.platform = cls.orig_platform
 
     def test_path_ok_blank(self):
         dialog = self.Dummy_HelpSource(' ')
@@ -242,6 +233,56 @@ def test_entry_ok_helpsource(self):
                 self.assertEqual(dialog.entry_ok(), result)
 
 
+# 2 CustomRun test classes each test one method.
+
+class CustomRunCLIargsokTest(unittest.TestCase):
+    "Test cli_ok method of the CustomRun subclass of Query."
+
+    class Dummy_CustomRun:
+        cli_args_ok = query.CustomRun.cli_args_ok
+        def __init__(self, dummy_entry):
+            self.entry = Var(value=dummy_entry)
+            self.entry_error = {'text': ''}
+        def showerror(self, message):
+            self.entry_error['text'] = message
+
+    def test_blank_args(self):
+        dialog = self.Dummy_CustomRun(' ')
+        self.assertEqual(dialog.cli_args_ok(), [])
+
+    def test_invalid_args(self):
+        dialog = self.Dummy_CustomRun("'no-closing-quote")
+        self.assertEqual(dialog.cli_args_ok(), None)
+        self.assertIn('No closing', dialog.entry_error['text'])
+
+    def test_good_args(self):
+        args = ['-n', '10', '--verbose', '-p', '/path', '--name']
+        dialog = self.Dummy_CustomRun(' '.join(args) + ' "my name"')
+        self.assertEqual(dialog.cli_args_ok(), args + ["my name"])
+        self.assertEqual(dialog.entry_error['text'], '')
+
+
+class CustomRunEntryokTest(unittest.TestCase):
+    "Test entry_ok method of the CustomRun subclass of Query."
+
+    class Dummy_CustomRun:
+        entry_ok = query.CustomRun.entry_ok
+        entry_error = {}
+        restartvar = Var()
+        def cli_args_ok(self):
+            return self.cli_args
+
+    def test_entry_ok_customrun(self):
+        dialog = self.Dummy_CustomRun()
+        for restart in {True, False}:
+            dialog.restartvar.set(restart)
+            for cli_args, result in ((None, None),
+                                     (['my arg'], (['my arg'], restart))):
+                with self.subTest(restart=restart, cli_args=cli_args):
+                    dialog.cli_args = cli_args
+                    self.assertEqual(dialog.entry_ok(), result)
+
+
 # GUI TESTS
 
 class QueryGuiTest(unittest.TestCase):
@@ -302,9 +343,7 @@ def test_click_section_name(self):
         dialog.entry.insert(0, 'okay')
         dialog.button_ok.invoke()
         self.assertEqual(dialog.result, 'okay')
-        del dialog
         root.destroy()
-        del root
 
 
 class ModulenameGuiTest(unittest.TestCase):
@@ -321,9 +360,7 @@ def test_click_module_name(self):
         self.assertEqual(dialog.entry.get(), 'idlelib')
         dialog.button_ok.invoke()
         self.assertTrue(dialog.result.endswith('__init__.py'))
-        del dialog
         root.destroy()
-        del root
 
 
 class HelpsourceGuiTest(unittest.TestCase):
@@ -343,9 +380,23 @@ def test_click_help_source(self):
         dialog.button_ok.invoke()
         prefix = "file://" if sys.platform == 'darwin' else ''
         Equal(dialog.result, ('__test__', prefix + __file__))
-        del dialog
         root.destroy()
-        del root
+
+
+class CustomRunGuiTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        requires('gui')
+
+    def test_click_args(self):
+        root = Tk()
+        root.withdraw()
+        dialog =  query.CustomRun(root, 'Title', _utest=True)
+        dialog.entry.insert(0, 'okay')
+        dialog.button_ok.invoke()
+        self.assertEqual(dialog.result, (['okay'], True))
+        root.destroy()
 
 
 if __name__ == '__main__':
diff --git a/Lib/idlelib/mainmenu.py b/Lib/idlelib/mainmenu.py
index f834220fc2bb..1b8dc475650d 100644
--- a/Lib/idlelib/mainmenu.py
+++ b/Lib/idlelib/mainmenu.py
@@ -76,6 +76,7 @@
    ('Python Shell', '<<open-python-shell>>'),
    ('C_heck Module', '<<check-module>>'),
    ('R_un Module', '<<run-module>>'),
+   ('Run... _Customized', '<<run-custom>>'),
    ]),
 
  ('shell', [
diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py
index f0b72553db87..9b3bb1d186b8 100644
--- a/Lib/idlelib/query.py
+++ b/Lib/idlelib/query.py
@@ -21,10 +21,11 @@
 
 import importlib
 import os
+import shlex
 from sys import executable, platform  # Platform is set for one test.
 
-from tkinter import Toplevel, StringVar, W, E, S
-from tkinter.ttk import Frame, Button, Entry, Label
+from tkinter import Toplevel, StringVar, BooleanVar, W, E, S
+from tkinter.ttk import Frame, Button, Entry, Label, Checkbutton
 from tkinter import filedialog
 from tkinter.font import Font
 
@@ -83,7 +84,7 @@ def __init__(self, parent, title, message, *, text0='', used_names={},
             self.deiconify()  # Unhide now that geometry set.
             self.wait_window()
 
-    def create_widgets(self):  # Call from override, if any.
+    def create_widgets(self, ok_text='OK'):  # Call from override, if any.
         # Bind to self widgets needed for entry_ok or unittest.
         self.frame = frame = Frame(self, padding=10)
         frame.grid(column=0, row=0, sticky='news')
@@ -99,7 +100,7 @@ def create_widgets(self):  # Call from override, if any.
         self.entry_error = Label(frame, text=' ', foreground='red',
                                  font=self.error_font)
         self.button_ok = Button(
-                frame, text='OK', default='active', command=self.ok)
+                frame, text=ok_text, default='active', command=self.ok)
         self.button_cancel = Button(
                 frame, text='Cancel', command=self.cancel)
 
@@ -302,10 +303,56 @@ def entry_ok(self):
         path = self.path_ok()
         return None if name is None or path is None else (name, path)
 
+class CustomRun(Query):
+    """Get settings for custom run of module.
+
+    1. Command line arguments to extend sys.argv.
+    2. Whether to restart Shell or not.
+    """
+    # Used in runscript.run_custom_event
+
+    def __init__(self, parent, title, *, cli_args='',
+                 _htest=False, _utest=False):
+        # TODO Use cli_args to pre-populate entry.
+        message = 'Command Line Arguments for sys.argv:'
+        super().__init__(
+                parent, title, message, text0=cli_args,
+                _htest=_htest, _utest=_utest)
+
+    def create_widgets(self):
+        super().create_widgets(ok_text='Run')
+        frame = self.frame
+        self.restartvar = BooleanVar(self, value=True)
+        restart = Checkbutton(frame, variable=self.restartvar, onvalue=True,
+                              offvalue=False, text='Restart shell')
+        self.args_error = Label(frame, text=' ', foreground='red',
+                                font=self.error_font)
+
+        restart.grid(column=0, row=4, columnspan=3, padx=5, sticky='w')
+        self.args_error.grid(column=0, row=12, columnspan=3, padx=5,
+                             sticky='we')
+
+    def cli_args_ok(self):
+        "Validity check and parsing for command line arguments."
+        cli_string = self.entry.get().strip()
+        try:
+            cli_args = shlex.split(cli_string, posix=True)
+        except ValueError as err:
+            self.showerror(str(err))
+            return None
+        return cli_args
+
+    def entry_ok(self):
+        "Return apparently valid (cli_args, restart) or None"
+        self.entry_error['text'] = ''
+        cli_args = self.cli_args_ok()
+        restart = self.restartvar.get()
+        return None if cli_args is None else (cli_args, restart)
+
 
 if __name__ == '__main__':
     from unittest import main
     main('idlelib.idle_test.test_query', verbosity=2, exit=False)
 
     from idlelib.idle_test.htest import run
-    run(Query, HelpSource)
+    run(Query, HelpSource, CustomRun)
diff --git a/Lib/idlelib/runscript.py b/Lib/idlelib/runscript.py
index 83433b1cf0a4..b041e56fb840 100644
--- a/Lib/idlelib/runscript.py
+++ b/Lib/idlelib/runscript.py
@@ -18,6 +18,7 @@
 from idlelib.config import idleConf
 from idlelib import macosx
 from idlelib import pyshell
+from idlelib.query import CustomRun
 
 indent_message = """Error: Inconsistent indentation detected!
 
@@ -108,20 +109,24 @@ def run_module_event(self, event):
             # tries to run a module using the keyboard shortcut
             # (the menu item works fine).
             self.editwin.text_frame.after(200,
-                lambda: self.editwin.text_frame.event_generate('<<run-module-event-2>>'))
+                lambda: self.editwin.text_frame.event_generate(
+                        '<<run-module-event-2>>'))
             return 'break'
         else:
             return self._run_module_event(event)
 
-    def _run_module_event(self, event):
+    def run_custom_event(self, event):
+        return self._run_module_event(event, customize=True)
+
+    def _run_module_event(self, event, *, customize=False):
         """Run the module after setting up the environment.
 
-        First check the syntax.  If OK, make sure the shell is active and
-        then transfer the arguments, set the run environment's working
-        directory to the directory of the module being executed and also
-        add that directory to its sys.path if not already included.
+        First check the syntax.  Next get customization.  If OK, make
+        sure the shell is active and then transfer the arguments, set
+        the run environment's working directory to the directory of the
+        module being executed and also add that directory to its
+        sys.path if not already included.
         """
-
         filename = self.getfilename()
         if not filename:
             return 'break'
@@ -130,23 +135,34 @@ def _run_module_event(self, event):
             return 'break'
         if not self.tabnanny(filename):
             return 'break'
+        if customize:
+            title = f"Customize {self.editwin.short_title()} Run"
+            run_args = CustomRun(self.shell.text, title).result
+            if not run_args:  # User cancelled.
+                return 'break'
+        cli_args, restart = run_args if customize else ([], True)
         interp = self.shell.interp
-        if pyshell.use_subprocess:
-            interp.restart_subprocess(with_cwd=False, filename=
-                        self.editwin._filename_to_unicode(filename))
+        if pyshell.use_subprocess and restart:
+            interp.restart_subprocess(
+                    with_cwd=False, filename=
+                    self.editwin._filename_to_unicode(filename))
         dirname = os.path.dirname(filename)
-        # XXX Too often this discards arguments the user just set...
-        interp.runcommand("""if 1:
+        argv = [filename]
+        if cli_args:
+            argv += cli_args
+        interp.runcommand(f"""if 1:
             __file__ = {filename!r}
             import sys as _sys
             from os.path import basename as _basename
+            argv = {argv!r}
             if (not _sys.argv or
-                _basename(_sys.argv[0]) != _basename(__file__)):
-                _sys.argv = [__file__]
+                _basename(_sys.argv[0]) != _basename(__file__) or
+                len(argv) > 1):
+                _sys.argv = argv
             import os as _os
             _os.chdir({dirname!r})
             del _sys, _basename, _os
-            \n""".format(filename=filename, dirname=dirname))
+            \n""")
         interp.prepend_syspath(filename)
         # XXX KBK 03Jul04 When run w/o subprocess, runtime warnings still
         #         go to __stderr__.  With subprocess, they go to the shell.
diff --git a/Misc/NEWS.d/next/IDLE/2019-06-03-00-39-29.bpo-5680.VCQfOO.rst b/Misc/NEWS.d/next/IDLE/2019-06-03-00-39-29.bpo-5680.VCQfOO.rst
new file mode 100644
index 000000000000..9fc642077488
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2019-06-03-00-39-29.bpo-5680.VCQfOO.rst
@@ -0,0 +1,3 @@
+Add 'Run... Customized' to the Run menu to run a module with customized
+settings.  Any 'command line arguments' entered are added to sys.argv.
+One can suppress the normal Shell main module restart.



More information about the Python-checkins mailing list