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.
This commit is contained in:
Cheryl Sabella 2019-06-17 22:24:10 -04:00 committed by Terry Jan Reedy
parent 7fb3190bcf
commit 201bc2d18b
11 changed files with 211 additions and 51 deletions

View File

@ -207,9 +207,13 @@ Strip trailing whitespace
Run menu (Editor window only) Run menu (Editor window only)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. _python-shell:
Python Shell Python Shell
Open or wake up the Python Shell window. Open or wake up the Python Shell window.
.. _check-module:
Check Module Check Module
Check the syntax of the module currently open in the Editor window. If the 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 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 there is a syntax error, the approximate location is indicated in the
Editor window. Editor window.
.. _run-module:
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 environment, then execute the module. Output is displayed in the Shell
window. Note that output requires use of ``print`` or ``write``. window. Note that output requires use of ``print`` or ``write``.
When execution is complete, the Shell retains focus and displays a prompt. 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 This is similar to executing a file with ``python -i file`` at a command
line. 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) Shell menu (Shell window only)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -63,6 +63,7 @@ force-open-calltip= <Control-Key-backslash>
format-paragraph= <Alt-Key-q> format-paragraph= <Alt-Key-q>
flash-paren= <Control-Key-0> flash-paren= <Control-Key-0>
run-module= <Key-F5> run-module= <Key-F5>
run-custom= <Shift-Key-F5>
check-module= <Alt-Key-x> check-module= <Alt-Key-x>
zoom-height= <Alt-Key-2> zoom-height= <Alt-Key-2>
@ -122,6 +123,7 @@ force-open-calltip= <Control-Key-backslash>
format-paragraph= <Alt-Key-q> format-paragraph= <Alt-Key-q>
flash-paren= <Control-Key-0> flash-paren= <Control-Key-0>
run-module= <Key-F5> run-module= <Key-F5>
run-custom= <Shift-Key-F5>
check-module= <Alt-Key-x> check-module= <Alt-Key-x>
zoom-height= <Alt-Key-2> zoom-height= <Alt-Key-2>
@ -181,6 +183,7 @@ force-open-calltip= <Control-Key-backslash>
format-paragraph= <Alt-Key-q> format-paragraph= <Alt-Key-q>
flash-paren= <Control-Key-0> flash-paren= <Control-Key-0>
run-module= <Key-F5> run-module= <Key-F5>
run-custom= <Shift-Key-F5>
check-module= <Alt-Key-x> check-module= <Alt-Key-x>
zoom-height= <Alt-Key-2> zoom-height= <Alt-Key-2>
@ -240,6 +243,7 @@ force-open-calltip= <Control-Key-backslash>
format-paragraph= <Option-Key-q> format-paragraph= <Option-Key-q>
flash-paren= <Control-Key-0> flash-paren= <Control-Key-0>
run-module= <Key-F5> run-module= <Key-F5>
run-custom= <Shift-Key-F5>
check-module= <Option-Key-x> check-module= <Option-Key-x>
zoom-height= <Option-Key-0> zoom-height= <Option-Key-0>
@ -300,5 +304,6 @@ force-open-calltip= <Control-Key-backslash>
format-paragraph= <Option-Key-q> format-paragraph= <Option-Key-q>
flash-paren= <Control-Key-0> flash-paren= <Control-Key-0>
run-module= <Key-F5> run-module= <Key-F5>
run-custom= <Shift-Key-F5>
check-module= <Option-Key-x> check-module= <Option-Key-x>
zoom-height= <Option-Key-0> zoom-height= <Option-Key-0>

View File

@ -591,7 +591,9 @@ class IdleConf:
former_extension_events = { # Those with user-configurable keys. former_extension_events = { # Those with user-configurable keys.
'<<force-open-completions>>', '<<expand-word>>', '<<force-open-completions>>', '<<expand-word>>',
'<<force-open-calltip>>', '<<flash-paren>>', '<<format-paragraph>>', '<<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): def GetCoreKeys(self, keySetName=None):
"""Return dict of core virtual-key keybindings for keySetName. """Return dict of core virtual-key keybindings for keySetName.
@ -658,6 +660,7 @@ class IdleConf:
'<<flash-paren>>': ['<Control-Key-0>'], '<<flash-paren>>': ['<Control-Key-0>'],
'<<format-paragraph>>': ['<Alt-Key-q>'], '<<format-paragraph>>': ['<Alt-Key-q>'],
'<<run-module>>': ['<Key-F5>'], '<<run-module>>': ['<Key-F5>'],
'<<run-custom>>': ['<Shift-Key-F5>'],
'<<check-module>>': ['<Alt-Key-x>'], '<<check-module>>': ['<Alt-Key-x>'],
'<<zoom-height>>': ['<Alt-Key-2>'], '<<zoom-height>>': ['<Alt-Key-2>'],
} }

View File

@ -304,6 +304,7 @@ class EditorWindow(object):
scriptbinding = ScriptBinding(self) scriptbinding = ScriptBinding(self)
text.bind("<<check-module>>", scriptbinding.check_module_event) text.bind("<<check-module>>", scriptbinding.check_module_event)
text.bind("<<run-module>>", scriptbinding.run_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) text.bind("<<do-rstrip>>", self.Rstrip(self).do_rstrip)
ctip = self.Calltip(self) ctip = self.Calltip(self)
text.bind("<<try-open-calltip>>", ctip.try_open_calltip_event) text.bind("<<try-open-calltip>>", ctip.try_open_calltip_event)

View File

@ -248,17 +248,21 @@ including lines within multiline strings.</dd>
</div> </div>
<div class="section" id="run-menu-editor-window-only"> <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> <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> <dt>Python Shell</dt>
<dd>Open or wake up the Python Shell window.</dd> <dd>Open or wake up the Python Shell window.</dd>
</dl>
<dl class="docutils" id="check-module">
<dt>Check Module</dt> <dt>Check Module</dt>
<dd>Check the syntax of the module currently open in the Editor window. If the <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 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 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 there is a syntax error, the approximate location is indicated in the
Editor window.</dd> Editor window.</dd>
</dl>
<dl class="docutils" id="run-module">
<dt>Run Module</dt> <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 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>. 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. When execution is complete, the Shell retains focus and displays a prompt.
@ -266,6 +270,12 @@ At this point, one may interactively explore the result of execution.
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 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> line.</dd>
</dl> </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>
<div class="section" id="shell-menu-shell-window-only"> <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> <h3>Shell menu (Shell window only)<a class="headerlink" href="#shell-menu-shell-window-only" title="Permalink to this headline"></a></h3>

View File

@ -108,6 +108,15 @@ _color_delegator_spec = {
"The default color scheme is in idlelib/config-highlight.def" "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 = { ConfigDialog_spec = {
'file': 'configdialog', 'file': 'configdialog',
'kwds': {'title': 'ConfigDialogTest', 'kwds': {'title': 'ConfigDialogTest',

View File

@ -1,4 +1,4 @@
"""Test query, coverage 91%). """Test query, coverage 93%).
Non-gui tests for Query, SectionName, ModuleName, and HelpSource use Non-gui tests for Query, SectionName, ModuleName, and HelpSource use
dummy versions that extract the non-gui methods and add other needed dummy versions that extract the non-gui methods and add other needed
@ -30,11 +30,9 @@ class QueryTest(unittest.TestCase):
ok = query.Query.ok ok = query.Query.ok
cancel = query.Query.cancel cancel = query.Query.cancel
# Add attributes and initialization needed for tests. # Add attributes and initialization needed for tests.
entry = Var()
entry_error = {}
def __init__(self, dummy_entry): def __init__(self, dummy_entry):
self.entry.set(dummy_entry) self.entry = Var(value=dummy_entry)
self.entry_error['text'] = '' self.entry_error = {'text': ''}
self.result = None self.result = None
self.destroyed = False self.destroyed = False
def showerror(self, message): def showerror(self, message):
@ -80,11 +78,9 @@ class SectionNameTest(unittest.TestCase):
class Dummy_SectionName: class Dummy_SectionName:
entry_ok = query.SectionName.entry_ok # Function being tested. entry_ok = query.SectionName.entry_ok # Function being tested.
used_names = ['used'] used_names = ['used']
entry = Var()
entry_error = {}
def __init__(self, dummy_entry): def __init__(self, dummy_entry):
self.entry.set(dummy_entry) self.entry = Var(value=dummy_entry)
self.entry_error['text'] = '' self.entry_error = {'text': ''}
def showerror(self, message): def showerror(self, message):
self.entry_error['text'] = message self.entry_error['text'] = message
@ -115,11 +111,9 @@ class ModuleNameTest(unittest.TestCase):
class Dummy_ModuleName: class Dummy_ModuleName:
entry_ok = query.ModuleName.entry_ok # Function being tested. entry_ok = query.ModuleName.entry_ok # Function being tested.
text0 = '' text0 = ''
entry = Var()
entry_error = {}
def __init__(self, dummy_entry): def __init__(self, dummy_entry):
self.entry.set(dummy_entry) self.entry = Var(value=dummy_entry)
self.entry_error['text'] = '' self.entry_error = {'text': ''}
def showerror(self, message): def showerror(self, message):
self.entry_error['text'] = message self.entry_error['text'] = message
@ -144,9 +138,7 @@ class ModuleNameTest(unittest.TestCase):
self.assertEqual(dialog.entry_error['text'], '') self.assertEqual(dialog.entry_error['text'], '')
# 3 HelpSource test classes each test one function. # 3 HelpSource test classes each test one method.
orig_platform = query.platform
class HelpsourceBrowsefileTest(unittest.TestCase): class HelpsourceBrowsefileTest(unittest.TestCase):
"Test browse_file method of ModuleName subclass of Query." "Test browse_file method of ModuleName subclass of Query."
@ -178,17 +170,16 @@ class HelpsourcePathokTest(unittest.TestCase):
class Dummy_HelpSource: class Dummy_HelpSource:
path_ok = query.HelpSource.path_ok path_ok = query.HelpSource.path_ok
path = Var()
path_error = {}
def __init__(self, dummy_path): def __init__(self, dummy_path):
self.path.set(dummy_path) self.path = Var(value=dummy_path)
self.path_error['text'] = '' self.path_error = {'text': ''}
def showerror(self, message, widget=None): def showerror(self, message, widget=None):
self.path_error['text'] = message self.path_error['text'] = message
orig_platform = query.platform # Set in test_path_ok_file.
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
query.platform = orig_platform query.platform = cls.orig_platform
def test_path_ok_blank(self): def test_path_ok_blank(self):
dialog = self.Dummy_HelpSource(' ') dialog = self.Dummy_HelpSource(' ')
@ -242,6 +233,56 @@ class HelpsourceEntryokTest(unittest.TestCase):
self.assertEqual(dialog.entry_ok(), result) 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 # GUI TESTS
class QueryGuiTest(unittest.TestCase): class QueryGuiTest(unittest.TestCase):
@ -302,9 +343,7 @@ class SectionnameGuiTest(unittest.TestCase):
dialog.entry.insert(0, 'okay') dialog.entry.insert(0, 'okay')
dialog.button_ok.invoke() dialog.button_ok.invoke()
self.assertEqual(dialog.result, 'okay') self.assertEqual(dialog.result, 'okay')
del dialog
root.destroy() root.destroy()
del root
class ModulenameGuiTest(unittest.TestCase): class ModulenameGuiTest(unittest.TestCase):
@ -321,9 +360,7 @@ class ModulenameGuiTest(unittest.TestCase):
self.assertEqual(dialog.entry.get(), 'idlelib') self.assertEqual(dialog.entry.get(), 'idlelib')
dialog.button_ok.invoke() dialog.button_ok.invoke()
self.assertTrue(dialog.result.endswith('__init__.py')) self.assertTrue(dialog.result.endswith('__init__.py'))
del dialog
root.destroy() root.destroy()
del root
class HelpsourceGuiTest(unittest.TestCase): class HelpsourceGuiTest(unittest.TestCase):
@ -343,9 +380,23 @@ class HelpsourceGuiTest(unittest.TestCase):
dialog.button_ok.invoke() dialog.button_ok.invoke()
prefix = "file://" if sys.platform == 'darwin' else '' prefix = "file://" if sys.platform == 'darwin' else ''
Equal(dialog.result, ('__test__', prefix + __file__)) Equal(dialog.result, ('__test__', prefix + __file__))
del dialog
root.destroy() 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__': if __name__ == '__main__':

View File

@ -76,6 +76,7 @@ menudefs = [
('Python Shell', '<<open-python-shell>>'), ('Python Shell', '<<open-python-shell>>'),
('C_heck Module', '<<check-module>>'), ('C_heck Module', '<<check-module>>'),
('R_un Module', '<<run-module>>'), ('R_un Module', '<<run-module>>'),
('Run... _Customized', '<<run-custom>>'),
]), ]),
('shell', [ ('shell', [

View File

@ -21,10 +21,11 @@ Subclass HelpSource gets menu item and path for additions to Help menu.
import importlib import importlib
import os import os
import shlex
from sys import executable, platform # Platform is set for one test. from sys import executable, platform # Platform is set for one test.
from tkinter import Toplevel, StringVar, W, E, S from tkinter import Toplevel, StringVar, BooleanVar, W, E, S
from tkinter.ttk import Frame, Button, Entry, Label from tkinter.ttk import Frame, Button, Entry, Label, Checkbutton
from tkinter import filedialog from tkinter import filedialog
from tkinter.font import Font from tkinter.font import Font
@ -83,7 +84,7 @@ class Query(Toplevel):
self.deiconify() # Unhide now that geometry set. self.deiconify() # Unhide now that geometry set.
self.wait_window() 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. # Bind to self widgets needed for entry_ok or unittest.
self.frame = frame = Frame(self, padding=10) self.frame = frame = Frame(self, padding=10)
frame.grid(column=0, row=0, sticky='news') frame.grid(column=0, row=0, sticky='news')
@ -99,7 +100,7 @@ class Query(Toplevel):
self.entry_error = Label(frame, text=' ', foreground='red', self.entry_error = Label(frame, text=' ', foreground='red',
font=self.error_font) font=self.error_font)
self.button_ok = Button( 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( self.button_cancel = Button(
frame, text='Cancel', command=self.cancel) frame, text='Cancel', command=self.cancel)
@ -302,10 +303,56 @@ class HelpSource(Query):
path = self.path_ok() path = self.path_ok()
return None if name is None or path is None else (name, path) 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__': if __name__ == '__main__':
from unittest import main from unittest import main
main('idlelib.idle_test.test_query', verbosity=2, exit=False) main('idlelib.idle_test.test_query', verbosity=2, exit=False)
from idlelib.idle_test.htest import run from idlelib.idle_test.htest import run
run(Query, HelpSource) run(Query, HelpSource, CustomRun)

View File

@ -18,6 +18,7 @@ import tkinter.messagebox as tkMessageBox
from idlelib.config import idleConf from idlelib.config import idleConf
from idlelib import macosx from idlelib import macosx
from idlelib import pyshell from idlelib import pyshell
from idlelib.query import CustomRun
indent_message = """Error: Inconsistent indentation detected! indent_message = """Error: Inconsistent indentation detected!
@ -108,20 +109,24 @@ class ScriptBinding:
# tries to run a module using the keyboard shortcut # tries to run a module using the keyboard shortcut
# (the menu item works fine). # (the menu item works fine).
self.editwin.text_frame.after(200, 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' return 'break'
else: else:
return self._run_module_event(event) 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. """Run the module after setting up the environment.
First check the syntax. If OK, make sure the shell is active and First check the syntax. Next get customization. If OK, make
then transfer the arguments, set the run environment's working sure the shell is active and then transfer the arguments, set
directory to the directory of the module being executed and also the run environment's working directory to the directory of the
add that directory to its sys.path if not already included. module being executed and also add that directory to its
sys.path if not already included.
""" """
filename = self.getfilename() filename = self.getfilename()
if not filename: if not filename:
return 'break' return 'break'
@ -130,23 +135,34 @@ class ScriptBinding:
return 'break' return 'break'
if not self.tabnanny(filename): if not self.tabnanny(filename):
return 'break' 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 interp = self.shell.interp
if pyshell.use_subprocess: if pyshell.use_subprocess and restart:
interp.restart_subprocess(with_cwd=False, filename= interp.restart_subprocess(
self.editwin._filename_to_unicode(filename)) with_cwd=False, filename=
self.editwin._filename_to_unicode(filename))
dirname = os.path.dirname(filename) dirname = os.path.dirname(filename)
# XXX Too often this discards arguments the user just set... argv = [filename]
interp.runcommand("""if 1: if cli_args:
argv += cli_args
interp.runcommand(f"""if 1:
__file__ = {filename!r} __file__ = {filename!r}
import sys as _sys import sys as _sys
from os.path import basename as _basename from os.path import basename as _basename
argv = {argv!r}
if (not _sys.argv or if (not _sys.argv or
_basename(_sys.argv[0]) != _basename(__file__)): _basename(_sys.argv[0]) != _basename(__file__) or
_sys.argv = [__file__] len(argv) > 1):
_sys.argv = argv
import os as _os import os as _os
_os.chdir({dirname!r}) _os.chdir({dirname!r})
del _sys, _basename, _os del _sys, _basename, _os
\n""".format(filename=filename, dirname=dirname)) \n""")
interp.prepend_syspath(filename) interp.prepend_syspath(filename)
# XXX KBK 03Jul04 When run w/o subprocess, runtime warnings still # XXX KBK 03Jul04 When run w/o subprocess, runtime warnings still
# go to __stderr__. With subprocess, they go to the shell. # go to __stderr__. With subprocess, they go to the shell.

View File

@ -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.