Skip to content

Commit 201bc2d

Browse files
csabellaterryjreedy
authored andcommitted
bpo-5680: IDLE: Customize running a module (pythonGH-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.
1 parent 7fb3190 commit 201bc2d

File tree

11 files changed

+211
-51
lines changed

11 files changed

+211
-51
lines changed

Doc/library/idle.rst

+15-1
Original file line numberDiff line numberDiff line change
@@ -207,25 +207,39 @@ Strip trailing whitespace
207207
Run menu (Editor window only)
208208
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
209209

210+
.. _python-shell:
211+
210212
Python Shell
211213
Open or wake up the Python Shell window.
212214

215+
.. _check-module:
216+
213217
Check Module
214218
Check the syntax of the module currently open in the Editor window. If the
215219
module has not been saved IDLE will either prompt the user to save or
216220
autosave, as selected in the General tab of the Idle Settings dialog. If
217221
there is a syntax error, the approximate location is indicated in the
218222
Editor window.
219223

224+
.. _run-module:
225+
220226
Run Module
221-
Do Check Module (above). If no error, restart the shell to clean the
227+
Do :ref:`Check Module <check-module>`. If no error, restart the shell to clean the
222228
environment, then execute the module. Output is displayed in the Shell
223229
window. Note that output requires use of ``print`` or ``write``.
224230
When execution is complete, the Shell retains focus and displays a prompt.
225231
At this point, one may interactively explore the result of execution.
226232
This is similar to executing a file with ``python -i file`` at a command
227233
line.
228234

235+
.. _run-custom:
236+
237+
Run... Customized
238+
Same as :ref:`Run Module <run-module>`, but run the module with customized
239+
settings. *Command Line Arguments* extend :data:`sys.argv` as if passed
240+
on a command line. The module can be run in the Shell without restarting.
241+
242+
229243
Shell menu (Shell window only)
230244
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
231245

Lib/idlelib/config-keys.def

+5
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ force-open-calltip= <Control-Key-backslash>
6363
format-paragraph= <Alt-Key-q>
6464
flash-paren= <Control-Key-0>
6565
run-module= <Key-F5>
66+
run-custom= <Shift-Key-F5>
6667
check-module= <Alt-Key-x>
6768
zoom-height= <Alt-Key-2>
6869

@@ -122,6 +123,7 @@ force-open-calltip= <Control-Key-backslash>
122123
format-paragraph= <Alt-Key-q>
123124
flash-paren= <Control-Key-0>
124125
run-module= <Key-F5>
126+
run-custom= <Shift-Key-F5>
125127
check-module= <Alt-Key-x>
126128
zoom-height= <Alt-Key-2>
127129

@@ -181,6 +183,7 @@ force-open-calltip= <Control-Key-backslash>
181183
format-paragraph= <Alt-Key-q>
182184
flash-paren= <Control-Key-0>
183185
run-module= <Key-F5>
186+
run-custom= <Shift-Key-F5>
184187
check-module= <Alt-Key-x>
185188
zoom-height= <Alt-Key-2>
186189

@@ -240,6 +243,7 @@ force-open-calltip= <Control-Key-backslash>
240243
format-paragraph= <Option-Key-q>
241244
flash-paren= <Control-Key-0>
242245
run-module= <Key-F5>
246+
run-custom= <Shift-Key-F5>
243247
check-module= <Option-Key-x>
244248
zoom-height= <Option-Key-0>
245249

@@ -300,5 +304,6 @@ force-open-calltip= <Control-Key-backslash>
300304
format-paragraph= <Option-Key-q>
301305
flash-paren= <Control-Key-0>
302306
run-module= <Key-F5>
307+
run-custom= <Shift-Key-F5>
303308
check-module= <Option-Key-x>
304309
zoom-height= <Option-Key-0>

Lib/idlelib/config.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,9 @@ def IsCoreBinding(self, virtualEvent):
591591
former_extension_events = { # Those with user-configurable keys.
592592
'<<force-open-completions>>', '<<expand-word>>',
593593
'<<force-open-calltip>>', '<<flash-paren>>', '<<format-paragraph>>',
594-
'<<run-module>>', '<<check-module>>', '<<zoom-height>>'}
594+
'<<run-module>>', '<<check-module>>', '<<zoom-height>>',
595+
'<<run-custom>>',
596+
}
595597

596598
def GetCoreKeys(self, keySetName=None):
597599
"""Return dict of core virtual-key keybindings for keySetName.
@@ -658,6 +660,7 @@ def GetCoreKeys(self, keySetName=None):
658660
'<<flash-paren>>': ['<Control-Key-0>'],
659661
'<<format-paragraph>>': ['<Alt-Key-q>'],
660662
'<<run-module>>': ['<Key-F5>'],
663+
'<<run-custom>>': ['<Shift-Key-F5>'],
661664
'<<check-module>>': ['<Alt-Key-x>'],
662665
'<<zoom-height>>': ['<Alt-Key-2>'],
663666
}

Lib/idlelib/editor.py

+1
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ def __init__(self, flist=None, filename=None, key=None, root=None):
304304
scriptbinding = ScriptBinding(self)
305305
text.bind("<<check-module>>", scriptbinding.check_module_event)
306306
text.bind("<<run-module>>", scriptbinding.run_module_event)
307+
text.bind("<<run-custom>>", scriptbinding.run_custom_event)
307308
text.bind("<<do-rstrip>>", self.Rstrip(self).do_rstrip)
308309
ctip = self.Calltip(self)
309310
text.bind("<<try-open-calltip>>", ctip.try_open_calltip_event)

Lib/idlelib/help.html

+12-2
Original file line numberDiff line numberDiff line change
@@ -248,24 +248,34 @@ <h3>Edit menu (Shell and Editor)<a class="headerlink" href="#edit-menu-shell-and
248248
</div>
249249
<div class="section" id="run-menu-editor-window-only">
250250
<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>
251-
<dl class="docutils">
251+
<dl class="docutils" id="python-shell">
252252
<dt>Python Shell</dt>
253253
<dd>Open or wake up the Python Shell window.</dd>
254+
</dl>
255+
<dl class="docutils" id="check-module">
254256
<dt>Check Module</dt>
255257
<dd>Check the syntax of the module currently open in the Editor window. If the
256258
module has not been saved IDLE will either prompt the user to save or
257259
autosave, as selected in the General tab of the Idle Settings dialog. If
258260
there is a syntax error, the approximate location is indicated in the
259261
Editor window.</dd>
262+
</dl>
263+
<dl class="docutils" id="run-module">
260264
<dt>Run Module</dt>
261-
<dd>Do Check Module (above). If no error, restart the shell to clean the
265+
<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
262266
environment, then execute the module. Output is displayed in the Shell
263267
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>.
264268
When execution is complete, the Shell retains focus and displays a prompt.
265269
At this point, one may interactively explore the result of execution.
266270
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
267271
line.</dd>
268272
</dl>
273+
<dl class="docutils" id="run-custom">
274+
<dt>Run… Customized</dt>
275+
<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
276+
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
277+
on a command line. The module can be run in the Shell without restarting.</dd>
278+
</dl>
269279
</div>
270280
<div class="section" id="shell-menu-shell-window-only">
271281
<h3>Shell menu (Shell window only)<a class="headerlink" href="#shell-menu-shell-window-only" title="Permalink to this headline"></a></h3>

Lib/idlelib/idle_test/htest.py

+9
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,15 @@ def _wrapper(parent): # htest #
108108
"The default color scheme is in idlelib/config-highlight.def"
109109
}
110110

111+
CustomRun_spec = {
112+
'file': 'query',
113+
'kwds': {'title': 'Custom Run Args',
114+
'_htest': True},
115+
'msg': "Enter with <Return> or [Ok]. Print valid entry to Shell\n"
116+
"Arguments are parsed into a list\n"
117+
"Close dialog with valid entry, <Escape>, [Cancel], [X]"
118+
}
119+
111120
ConfigDialog_spec = {
112121
'file': 'configdialog',
113122
'kwds': {'title': 'ConfigDialogTest',

Lib/idlelib/idle_test/test_query.py

+78-27
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Test query, coverage 91%).
1+
"""Test query, coverage 93%).
22
33
Non-gui tests for Query, SectionName, ModuleName, and HelpSource use
44
dummy versions that extract the non-gui methods and add other needed
@@ -30,11 +30,9 @@ class Dummy_Query:
3030
ok = query.Query.ok
3131
cancel = query.Query.cancel
3232
# Add attributes and initialization needed for tests.
33-
entry = Var()
34-
entry_error = {}
3533
def __init__(self, dummy_entry):
36-
self.entry.set(dummy_entry)
37-
self.entry_error['text'] = ''
34+
self.entry = Var(value=dummy_entry)
35+
self.entry_error = {'text': ''}
3836
self.result = None
3937
self.destroyed = False
4038
def showerror(self, message):
@@ -80,11 +78,9 @@ class SectionNameTest(unittest.TestCase):
8078
class Dummy_SectionName:
8179
entry_ok = query.SectionName.entry_ok # Function being tested.
8280
used_names = ['used']
83-
entry = Var()
84-
entry_error = {}
8581
def __init__(self, dummy_entry):
86-
self.entry.set(dummy_entry)
87-
self.entry_error['text'] = ''
82+
self.entry = Var(value=dummy_entry)
83+
self.entry_error = {'text': ''}
8884
def showerror(self, message):
8985
self.entry_error['text'] = message
9086

@@ -115,11 +111,9 @@ class ModuleNameTest(unittest.TestCase):
115111
class Dummy_ModuleName:
116112
entry_ok = query.ModuleName.entry_ok # Function being tested.
117113
text0 = ''
118-
entry = Var()
119-
entry_error = {}
120114
def __init__(self, dummy_entry):
121-
self.entry.set(dummy_entry)
122-
self.entry_error['text'] = ''
115+
self.entry = Var(value=dummy_entry)
116+
self.entry_error = {'text': ''}
123117
def showerror(self, message):
124118
self.entry_error['text'] = message
125119

@@ -144,9 +138,7 @@ def test_good_module_name(self):
144138
self.assertEqual(dialog.entry_error['text'], '')
145139

146140

147-
# 3 HelpSource test classes each test one function.
148-
149-
orig_platform = query.platform
141+
# 3 HelpSource test classes each test one method.
150142

151143
class HelpsourceBrowsefileTest(unittest.TestCase):
152144
"Test browse_file method of ModuleName subclass of Query."
@@ -178,17 +170,16 @@ class HelpsourcePathokTest(unittest.TestCase):
178170

179171
class Dummy_HelpSource:
180172
path_ok = query.HelpSource.path_ok
181-
path = Var()
182-
path_error = {}
183173
def __init__(self, dummy_path):
184-
self.path.set(dummy_path)
185-
self.path_error['text'] = ''
174+
self.path = Var(value=dummy_path)
175+
self.path_error = {'text': ''}
186176
def showerror(self, message, widget=None):
187177
self.path_error['text'] = message
188178

179+
orig_platform = query.platform # Set in test_path_ok_file.
189180
@classmethod
190181
def tearDownClass(cls):
191-
query.platform = orig_platform
182+
query.platform = cls.orig_platform
192183

193184
def test_path_ok_blank(self):
194185
dialog = self.Dummy_HelpSource(' ')
@@ -242,6 +233,56 @@ def test_entry_ok_helpsource(self):
242233
self.assertEqual(dialog.entry_ok(), result)
243234

244235

236+
# 2 CustomRun test classes each test one method.
237+
238+
class CustomRunCLIargsokTest(unittest.TestCase):
239+
"Test cli_ok method of the CustomRun subclass of Query."
240+
241+
class Dummy_CustomRun:
242+
cli_args_ok = query.CustomRun.cli_args_ok
243+
def __init__(self, dummy_entry):
244+
self.entry = Var(value=dummy_entry)
245+
self.entry_error = {'text': ''}
246+
def showerror(self, message):
247+
self.entry_error['text'] = message
248+
249+
def test_blank_args(self):
250+
dialog = self.Dummy_CustomRun(' ')
251+
self.assertEqual(dialog.cli_args_ok(), [])
252+
253+
def test_invalid_args(self):
254+
dialog = self.Dummy_CustomRun("'no-closing-quote")
255+
self.assertEqual(dialog.cli_args_ok(), None)
256+
self.assertIn('No closing', dialog.entry_error['text'])
257+
258+
def test_good_args(self):
259+
args = ['-n', '10', '--verbose', '-p', '/path', '--name']
260+
dialog = self.Dummy_CustomRun(' '.join(args) + ' "my name"')
261+
self.assertEqual(dialog.cli_args_ok(), args + ["my name"])
262+
self.assertEqual(dialog.entry_error['text'], '')
263+
264+
265+
class CustomRunEntryokTest(unittest.TestCase):
266+
"Test entry_ok method of the CustomRun subclass of Query."
267+
268+
class Dummy_CustomRun:
269+
entry_ok = query.CustomRun.entry_ok
270+
entry_error = {}
271+
restartvar = Var()
272+
def cli_args_ok(self):
273+
return self.cli_args
274+
275+
def test_entry_ok_customrun(self):
276+
dialog = self.Dummy_CustomRun()
277+
for restart in {True, False}:
278+
dialog.restartvar.set(restart)
279+
for cli_args, result in ((None, None),
280+
(['my arg'], (['my arg'], restart))):
281+
with self.subTest(restart=restart, cli_args=cli_args):
282+
dialog.cli_args = cli_args
283+
self.assertEqual(dialog.entry_ok(), result)
284+
285+
245286
# GUI TESTS
246287

247288
class QueryGuiTest(unittest.TestCase):
@@ -302,9 +343,7 @@ def test_click_section_name(self):
302343
dialog.entry.insert(0, 'okay')
303344
dialog.button_ok.invoke()
304345
self.assertEqual(dialog.result, 'okay')
305-
del dialog
306346
root.destroy()
307-
del root
308347

309348

310349
class ModulenameGuiTest(unittest.TestCase):
@@ -321,9 +360,7 @@ def test_click_module_name(self):
321360
self.assertEqual(dialog.entry.get(), 'idlelib')
322361
dialog.button_ok.invoke()
323362
self.assertTrue(dialog.result.endswith('__init__.py'))
324-
del dialog
325363
root.destroy()
326-
del root
327364

328365

329366
class HelpsourceGuiTest(unittest.TestCase):
@@ -343,9 +380,23 @@ def test_click_help_source(self):
343380
dialog.button_ok.invoke()
344381
prefix = "file://" if sys.platform == 'darwin' else ''
345382
Equal(dialog.result, ('__test__', prefix + __file__))
346-
del dialog
347383
root.destroy()
348-
del root
384+
385+
386+
class CustomRunGuiTest(unittest.TestCase):
387+
388+
@classmethod
389+
def setUpClass(cls):
390+
requires('gui')
391+
392+
def test_click_args(self):
393+
root = Tk()
394+
root.withdraw()
395+
dialog = query.CustomRun(root, 'Title', _utest=True)
396+
dialog.entry.insert(0, 'okay')
397+
dialog.button_ok.invoke()
398+
self.assertEqual(dialog.result, (['okay'], True))
399+
root.destroy()
349400

350401

351402
if __name__ == '__main__':

Lib/idlelib/mainmenu.py

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
('Python Shell', '<<open-python-shell>>'),
7777
('C_heck Module', '<<check-module>>'),
7878
('R_un Module', '<<run-module>>'),
79+
('Run... _Customized', '<<run-custom>>'),
7980
]),
8081

8182
('shell', [

0 commit comments

Comments
 (0)