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:
parent
7fb3190bcf
commit
201bc2d18b
|
@ -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)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>'],
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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__':
|
||||||
|
|
|
@ -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', [
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue