From c242ad4f5e78ef6548e9aac15b0386e044df0f97 Mon Sep 17 00:00:00 2001 From: Cheryl Sabella Date: Sun, 4 Feb 2018 11:30:16 -0500 Subject: [PATCH 1/6] Add Startup tab to configdialog --- Lib/idlelib/config-main.def | 6 + Lib/idlelib/configdialog.py | 231 ++++++++++++++++----- Lib/idlelib/idle_test/test_config.py | 4 +- Lib/idlelib/idle_test/test_configdialog.py | 132 +++++++++--- 4 files changed, 288 insertions(+), 85 deletions(-) diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def index 16f4b0959cf13c..11aeb0e564b13e 100644 --- a/Lib/idlelib/config-main.def +++ b/Lib/idlelib/config-main.def @@ -65,6 +65,12 @@ font= TkFixedFont font-size= 10 font-bold= 0 encoding= none +editor-template-code= + +[ShellWindow] +startup-code-on= False +restart-code-on= False +shell-startup-code= [Indent] use-spaces= 1 diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 36ac4a23a52dc0..53be53a326ed97 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -109,11 +109,13 @@ def create_widgets(self): self.fontpage = FontPage(note, self.highpage) self.keyspage = KeysPage(note) self.genpage = GenPage(note) + self.startuppage = StartupPage(note) self.extpage = self.create_page_extensions() note.add(self.fontpage, text='Fonts/Tabs') note.add(self.highpage, text='Highlights') note.add(self.keyspage, text=' Keys ') note.add(self.genpage, text=' General ') + note.add(self.startuppage, text=' Startup ') note.add(self.extpage, text='Extensions') note.enable_traversal() note.pack(side=TOP, expand=TRUE, fill=BOTH) @@ -1766,10 +1768,8 @@ def create_page_general(self): Enable users to provisionally change general options. Function load_general_cfg intializes tk variables and helplist using - idleConf. Radiobuttons startup_shell_on and startup_editor_on - set var startup_edit. Radiobuttons save_ask_on and save_auto_on - set var autosave. Entry boxes win_width_int and win_height_int - set var win_width and win_height. Setting var_name invokes the + idleConf. Radiobuttons save_ask_on and save_auto_on + set var autosave. Setting var_name invokes the default callback that adds option to changes. Helplist: load_general_cfg loads list user_helplist with @@ -1782,16 +1782,6 @@ def create_page_general(self): Widgets for GenPage(Frame): (*) widgets bound to self frame_window: LabelFrame - frame_run: Frame - startup_title: Label - (*)startup_editor_on: Radiobutton - startup_edit - (*)startup_shell_on: Radiobutton - startup_edit - frame_win_size: Frame - win_size_title: Label - win_width_title: Label - (*)win_width_int: Entry - win_width - win_height_title: Label - (*)win_height_int: Entry - win_height frame_autocomplete: Frame auto_wait_title: Label (*)auto_wait_int: Entry - autocomplete_wait @@ -1823,12 +1813,6 @@ def create_page_general(self): scroll_helplist: Scrollbar """ # Integer values need StringVar because int('') raises. - self.startup_edit = tracers.add( - IntVar(self), ('main', 'General', 'editor-on-startup')) - self.win_width = tracers.add( - StringVar(self), ('main', 'EditorWindow', 'width')) - self.win_height = tracers.add( - StringVar(self), ('main', 'EditorWindow', 'height')) self.autocomplete_wait = tracers.add( StringVar(self), ('extensions', 'AutoComplete', 'popupwait')) self.paren_style = tracers.add( @@ -1854,25 +1838,6 @@ def create_page_general(self): frame_help = LabelFrame(self, borderwidth=2, relief=GROOVE, text=' Additional Help Sources ') # Frame_window. - frame_run = Frame(frame_window, borderwidth=0) - startup_title = Label(frame_run, text='At Startup') - self.startup_editor_on = Radiobutton( - frame_run, variable=self.startup_edit, value=1, - text="Open Edit Window") - self.startup_shell_on = Radiobutton( - frame_run, variable=self.startup_edit, value=0, - text='Open Shell Window') - - frame_win_size = Frame(frame_window, borderwidth=0) - win_size_title = Label( - frame_win_size, text='Initial Window Size (in characters)') - win_width_title = Label(frame_win_size, text='Width') - self.win_width_int = Entry( - frame_win_size, textvariable=self.win_width, width=3) - win_height_title = Label(frame_win_size, text='Height') - self.win_height_int = Entry( - frame_win_size, textvariable=self.win_height, width=3) - frame_autocomplete = Frame(frame_window, borderwidth=0,) auto_wait_title = Label(frame_autocomplete, text='Completions Popup Wait (milliseconds)') @@ -1940,18 +1905,6 @@ def create_page_general(self): frame_window.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) frame_editor.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - # frame_run. - frame_run.pack(side=TOP, padx=5, pady=0, fill=X) - startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5) - self.startup_shell_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) - self.startup_editor_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) - # frame_win_size. - frame_win_size.pack(side=TOP, padx=5, pady=0, fill=X) - win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5) - self.win_height_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) - win_height_title.pack(side=RIGHT, anchor=E, pady=5) - self.win_width_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) - win_width_title.pack(side=RIGHT, anchor=E, pady=5) # frame_autocomplete. frame_autocomplete.pack(side=TOP, padx=5, pady=0, fill=X) auto_wait_title.pack(side=LEFT, anchor=W, padx=5, pady=5) @@ -1991,12 +1944,6 @@ def create_page_general(self): def load_general_cfg(self): "Load current configuration settings for the general options." # Set variables for all windows. - self.startup_edit.set(idleConf.GetOption( - 'main', 'General', 'editor-on-startup', type='bool')) - self.win_width.set(idleConf.GetOption( - 'main', 'EditorWindow', 'width', type='int')) - self.win_height.set(idleConf.GetOption( - 'main', 'EditorWindow', 'height', type='int')) self.autocomplete_wait.set(idleConf.GetOption( 'extensions', 'AutoComplete', 'popupwait', type='int')) self.paren_style.set(idleConf.GetOption( @@ -2090,6 +2037,176 @@ def update_help_changes(self): ';'.join(self.user_helplist[num-1][:2])) +class StartupPage(Frame): + + def __init__(self, master): + super().__init__(master) + self.create_page_startup() + self.load_startup_cfg() + + def create_page_startup(self): + """Return frame of widgets for Startup tab. + + Enable users to provisionally change startup options. Function + load_startup_cfg intializes tk variables using idleConf. + Radiobuttons startup_shell_on and startup_editor_on + set var startup_edit. Entry boxes win_width_int and win_height_int + set var win_width and win_height. Setting var_name invokes the + default callback that adds option to changes. + + Widgets for StartupPage(Frame): (*) widgets bound to self + frame_window: LabelFrame + frame_run: Frame + startup_title: Label + (*)startup_editor_on: Radiobutton - startup_edit + (*)startup_shell_on: Radiobutton - startup_edit + frame_win_size: Frame + win_size_title: Label + win_width_title: Label + (*)win_width_int: Entry - win_width + win_height_title: Label + (*)win_height_int: Entry - win_height + frame_code: Frame + frame_code_shell: Frame + (*)shell_startup_toggle: Checkbutton - startup_code_on + (*)shell_restart_toggle: Checkbutton - restart_code_on + (*)shell_startup_text: Text + frame_code_editor: Frame + (*)editor_template_text: Text + """ + # Integer values need StringVar because int('') raises. + self.startup_edit = tracers.add( + IntVar(self), ('main', 'General', 'editor-on-startup')) + self.win_width = tracers.add( + StringVar(self), ('main', 'EditorWindow', 'width')) + self.win_height = tracers.add( + StringVar(self), ('main', 'EditorWindow', 'height')) + self.startup_code_on = tracers.add( + BooleanVar(self), ('main', 'ShellWindow', 'startup-code-on')) + self.restart_code_on = tracers.add( + BooleanVar(self), ('main', 'ShellWindow', 'restart-code-on')) + + # Create widgets: + # Section frames. + frame_window = LabelFrame(self, borderwidth=2, relief=GROOVE, + text=' Window Preferences') + frame_shell = LabelFrame(self, borderwidth=2, relief=GROOVE, + text=' Shell Startup Code ') + frame_editor = LabelFrame(self, borderwidth=2, relief=GROOVE, + text=' Editor Template Code ') + + # Frame_window. + frame_run = Frame(frame_window, borderwidth=0) + startup_title = Label(frame_run, text='At Startup') + self.startup_editor_on = Radiobutton( + frame_run, variable=self.startup_edit, value=1, + text="Open Edit Window") + self.startup_shell_on = Radiobutton( + frame_run, variable=self.startup_edit, value=0, + text='Open Shell Window') + + frame_win_size = Frame(frame_window, borderwidth=0) + win_size_title = Label( + frame_win_size, text='Initial Window Size (in characters)') + win_width_title = Label(frame_win_size, text='Width') + self.win_width_int = Entry( + frame_win_size, textvariable=self.win_width, width=3) + win_height_title = Label(frame_win_size, text='Height') + self.win_height_int = Entry( + frame_win_size, textvariable=self.win_height, width=3) + + # Frame_shell. + frame_shell_toggle = Frame(frame_shell) + self.shell_startup_toggle = Checkbutton(frame_shell_toggle, + variable=self.startup_code_on, + onvalue=True, offvalue=False, + text='Run code on startup') + self.shell_restart_toggle = Checkbutton(frame_shell_toggle, + variable=self.restart_code_on, + onvalue=True, offvalue=False, + text='Run code on restart') + self.shell_startup_text = Text(frame_shell, width=20, height=10) + scroll_shell = Scrollbar(frame_shell) + scroll_shell['command'] = self.shell_startup_text.yview + self.shell_startup_text['yscrollcommand'] = scroll_shell.set + + # Frame_editor. + self.editor_template_text = Text(frame_editor, width=20, height=10) + scroll_editor = Scrollbar(frame_editor) + scroll_editor['command'] = self.editor_template_text.yview + self.editor_template_text['yscrollcommand'] = scroll_editor.set + + # Pack widgets: + # Body. + frame_window.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + # frame_run. + frame_run.pack(side=TOP, padx=5, pady=0, fill=X) + startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.startup_shell_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) + self.startup_editor_on.pack(side=RIGHT, anchor=W, padx=5, pady=5) + # frame_win_size. + frame_win_size.pack(side=TOP, padx=5, pady=0, fill=X) + win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.win_height_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) + win_height_title.pack(side=RIGHT, anchor=E, pady=5) + self.win_width_int.pack(side=RIGHT, anchor=E, padx=10, pady=5) + win_width_title.pack(side=RIGHT, anchor=E, pady=5) + # Frame_shell. + frame_shell.pack(side='top', padx=5, pady=5, expand=True, fill='both') + frame_shell_toggle.pack(side='top') + self.shell_startup_toggle.pack(side='left', padx=2, pady=2) + self.shell_restart_toggle.pack(side='right', padx=2, pady=2) + scroll_shell.pack(side='right', anchor='w', fill='y') + self.shell_startup_text.pack(fill='x') + # Frame_editor. + frame_editor.pack(side='top', padx=5, pady=0, fill='x') + scroll_editor.pack(side='right', anchor='w', fill='y') + self.editor_template_text.pack(fill='x') + + def load_startup_cfg(self): + "Load current configuration settings for the startup options." + # Set variables for all windows. + self.startup_edit.set(idleConf.GetOption( + 'main', 'General', 'editor-on-startup', type='bool')) + self.win_width.set(idleConf.GetOption( + 'main', 'EditorWindow', 'width', type='int')) + self.win_height.set(idleConf.GetOption( + 'main', 'EditorWindow', 'height', type='int')) + + self.startup_code_on.set(idleConf.GetOption( + 'main', 'ShellWindow', 'startup-code-on', type='bool')) + self.restart_code_on.set(idleConf.GetOption( + 'main', 'ShellWindow', 'restart-code-on', type='bool')) + shell_code = idleConf.GetOption( + 'main', 'ShellWindow', 'shell-startup-code') + self.shell_startup_text.insert('end', shell_code or '') + self.shell_startup_text.edit_modified(False) + # Text widgets don't have trace methods, but instead set a + # modified flag on inserts or deletes. + self.shell_startup_text.bind('<>', + self.var_changed_shell_text) + + editor_template = idleConf.GetOption( + 'main', 'EditorWindow', 'editor-template-code') + self.editor_template_text.insert('end', editor_template or '') + self.editor_template_text.edit_modified(False) + self.editor_template_text.bind('<>', + self.var_changed_editor_template) + + def var_changed_shell_text(self, *params): + "Store changes to shell startup text." + value = self.shell_startup_text.get('1.0', 'end-1c') + changes.add_option('main', 'ShellWindow', 'shell-startup-code', value) + self.shell_startup_text.edit_modified(False) + + def var_changed_editor_template(self, *params): + "Store changes to editor template text." + value = self.editor_template_text.get('1.0', 'end-1c') + changes.add_option('main', 'EditorWindow', + 'editor-template-code', value) + self.editor_template_text.edit_modified(False) + + class VarTrace: """Maintain Tk variables trace state.""" diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index abfec7993e0744..89e778df9a0630 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -357,11 +357,11 @@ def test_get_section_list(self): self.assertCountEqual( conf.GetSectionList('default', 'main'), - ['General', 'EditorWindow', 'Indent', 'Theme', + ['General', 'EditorWindow', 'ShellWindow', 'Indent', 'Theme', 'Keys', 'History', 'HelpFiles']) self.assertCountEqual( conf.GetSectionList('user', 'main'), - ['General', 'EditorWindow', 'Indent', 'Theme', + ['General', 'EditorWindow', 'ShellWindow', 'Indent', 'Theme', 'Keys', 'History', 'HelpFiles']) with self.assertRaises(config.InvalidConfigSet): diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 982dc0b7eff7e1..8552bf77df9e01 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -1102,41 +1102,15 @@ def test_load_general_cfg(self): # Set to wrong values, load, check right values. eq = self.assertEqual d = self.page - d.startup_edit.set(1) d.autosave.set(1) - d.win_width.set(1) - d.win_height.set(1) d.helplist.insert('end', 'bad') d.user_helplist = ['bad', 'worse'] idleConf.SetOption('main', 'HelpFiles', '1', 'name;file') d.load_general_cfg() - eq(d.startup_edit.get(), 0) eq(d.autosave.get(), 0) - eq(d.win_width.get(), '80') - eq(d.win_height.get(), '40') eq(d.helplist.get(0, 'end'), ('name',)) eq(d.user_helplist, [('name', 'file', '1')]) - def test_startup(self): - d = self.page - d.startup_editor_on.invoke() - self.assertEqual(mainpage, - {'General': {'editor-on-startup': '1'}}) - changes.clear() - d.startup_shell_on.invoke() - self.assertEqual(mainpage, - {'General': {'editor-on-startup': '0'}}) - - def test_editor_size(self): - d = self.page - d.win_height_int.delete(0, 'end') - d.win_height_int.insert(0, '11') - self.assertEqual(mainpage, {'EditorWindow': {'height': '11'}}) - changes.clear() - d.win_width_int.delete(0, 'end') - d.win_width_int.insert(0, '11') - self.assertEqual(mainpage, {'EditorWindow': {'width': '11'}}) - def test_autocomplete_wait(self): self.page.auto_wait_int.delete(0, 'end') self.page.auto_wait_int.insert(0, '11') @@ -1307,6 +1281,112 @@ def test_update_help_changes(self): d.update_help_changes = Func() +class StartupPageTest(unittest.TestCase): + """Test that startup tab widgets enable users to make changes. + + Test that widget actions set vars and that var changes add + options. + """ + @classmethod + def setUpClass(cls): + page = cls.page = dialog.startuppage + dialog.note.select(page) + + @classmethod + def tearDownClass(cls): + page = cls.page + + def setUp(self): + changes.clear() + + def test_load_startup_cfg(self): + # Set to wrong values, load, check right values. + eq = self.assertEqual + d = self.page + d.startup_edit.set(1) + d.win_width.set(1) + d.win_height.set(1) + d.startup_code_on.set(1) + d.restart_code_on.set(1) + d.shell_startup_text.insert('end', 'shell spam') + d.editor_template_text.insert('end', 'editor spam') + d.load_startup_cfg() + eq(d.startup_edit.get(), 0) + eq(d.win_width.get(), '80') + eq(d.win_height.get(), '40') + eq(d.startup_code_on.get(), False) + eq(d.restart_code_on.get(), False) + self.assertNotIn('spam', d.shell_startup_text.get('1.0')) + self.assertNotIn('spam', d.editor_template_text.get('1.0')) + + def test_startup(self): + d = self.page + d.startup_editor_on.invoke() + self.assertEqual(mainpage, + {'General': {'editor-on-startup': '1'}}) + changes.clear() + d.startup_shell_on.invoke() + self.assertEqual(mainpage, + {'General': {'editor-on-startup': '0'}}) + + def test_editor_size(self): + d = self.page + d.win_height_int.delete(0, 'end') + d.win_height_int.insert(0, '11') + self.assertEqual(mainpage, {'EditorWindow': {'height': '11'}}) + changes.clear() + d.win_width_int.delete(0, 'end') + d.win_width_int.insert(0, '11') + self.assertEqual(mainpage, {'EditorWindow': {'width': '11'}}) + + def test_startup_code_on(self): + d = self.page + d.shell_startup_toggle.invoke() + self.assertEqual(mainpage, + {'ShellWindow': {'startup-code-on': 'True'}}) + changes.clear() + d.shell_startup_toggle.invoke() + self.assertEqual(mainpage, + {'ShellWindow': {'startup-code-on': 'False'}}) + + def test_restart_code_on(self): + d = self.page + d.shell_restart_toggle.invoke() + self.assertEqual(mainpage, + {'ShellWindow': {'restart-code-on': 'True'}}) + changes.clear() + d.shell_restart_toggle.invoke() + self.assertEqual(mainpage, + {'ShellWindow': {'restart-code-on': 'False'}}) + + def test_shell_startup_text(self): + d = self.page + d.shell_startup_text.delete('1.0', 'end') + text = '''import os\n + import re''' + d.shell_startup_text.insert('end', text) + self.assertEqual(mainpage, + {'ShellWindow': {'shell-startup-code': text}}) + changes.clear() + d.shell_startup_text.delete('1.0', 'end') + self.assertEqual(mainpage, + {'ShellWindow': {'shell-startup-code': ''}}) + + def test_editor_template_text(self): + d = self.page + d.editor_template_text.delete('1.0', 'end') + text = '''# This is a comment.\n + # Second line''' + d.editor_template_text.insert('end', text) + self.assertEqual( + mainpage, + {'EditorWindow': {'editor-template-code': text}}) + changes.clear() + d.editor_template_text.delete('1.0', 'end') + self.assertEqual(mainpage, + {'EditorWindow': {'editor-template-code': ''}}) + + class VarTraceTest(unittest.TestCase): @classmethod From 583620ce9952952bc5ff857398973fe623b01f84 Mon Sep 17 00:00:00 2001 From: Cheryl Sabella Date: Sun, 4 Feb 2018 18:45:49 -0500 Subject: [PATCH 2/6] Populate new empty editor window with config template. --- Lib/idlelib/editor.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index b51c45c97e50f2..ea36c60fa37666 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -332,8 +332,15 @@ def _filename_to_unicode(self, filename): return re.sub('[\U00010000-\U0010FFFF]', '\ufffd', filename) def new_callback(self, event): + """Create a new editor window. + + A new editor is created with default template text. + """ dirname, basename = self.io.defaultfilename() - self.flist.new(dirname) + editor = self.flist.new(dirname) + template = idleConf.GetOption('main', 'EditorWindow', + 'editor-template-code') + editor.text.insert('end', template or '') return "break" def home_callback(self, event): From 9fcde28bbc2c783c7baa7b81d20ec788455ad7c9 Mon Sep 17 00:00:00 2001 From: Cheryl Sabella Date: Sun, 4 Feb 2018 18:46:55 -0500 Subject: [PATCH 3/6] Run code from config file on initial startup --- Lib/idlelib/pyshell.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 8b07d52cc4872a..5637b7543e6aae 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -1382,6 +1382,7 @@ def main(): debug = False cmd = None script = None + config_startup_code = None startup = False try: opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:") @@ -1445,11 +1446,16 @@ def main(): dir = os.getcwd() if dir not in sys.path: sys.path.insert(0, dir) - # check the IDLE settings configuration (but command line overrides) + # Check the IDLE settings configuration (but command line overrides). edit_start = idleConf.GetOption('main', 'General', 'editor-on-startup', type='bool') enable_edit = enable_edit or edit_start enable_shell = enable_shell or not enable_edit + if idleConf.GetOption('main', 'ShellWindow', + 'startup-code-on', type='bool'): + config_startup_code = idleConf.GetOption( + 'main', 'ShellWindow', 'shell-startup-code') + sys.argv = ['-c'] + [config_startup_code] # Setup root. Don't break user code run in IDLE process. # Don't change environment when testing. @@ -1510,7 +1516,7 @@ def main(): os.environ.get("PYTHONSTARTUP") if filename and os.path.isfile(filename): shell.interp.execfile(filename) - if cmd or script: + if cmd or script or config_startup_code: shell.interp.runcommand("""if 1: import sys as _sys _sys.argv = %r @@ -1521,6 +1527,8 @@ def main(): elif script: shell.interp.prepend_syspath(script) shell.interp.execfile(script) + elif config_startup_code: + shell.interp.execsource(config_startup_code) elif shell: # If there is a shell window and no cmd or script in progress, # check for problematic OS X Tk versions and print a warning From fe8b557399fc38829f73f8fe70c7109bc841294d Mon Sep 17 00:00:00 2001 From: Cheryl Sabella Date: Sun, 4 Feb 2018 19:31:25 -0500 Subject: [PATCH 4/6] Run code from config file on shell restart --- Lib/idlelib/pyshell.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 5637b7543e6aae..94cfb86924023c 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -495,6 +495,11 @@ def restart_subprocess(self, with_cwd=False, filename=''): # reload remote debugger breakpoints for all PyShellEditWindows debug.load_breakpoints() self.compile.compiler.flags = self.original_compiler_flags + if idleConf.GetOption('main', 'ShellWindow', + 'restart-code-on', type='bool'): + config_startup_code = idleConf.GetOption( + 'main', 'ShellWindow', 'shell-startup-code') + self.execsource(config_startup_code) self.restarting = False return self.rpcclt From c18990c9220be7853923ee40898dd8d3e94755e3 Mon Sep 17 00:00:00 2001 From: Cheryl Sabella Date: Sun, 4 Feb 2018 20:11:00 -0500 Subject: [PATCH 5/6] Add blurb and docs --- Doc/library/idle.rst | 24 +++++++++++++++---- .../2018-02-04-19-45-46.bpo-5594.Ydp0aG.rst | 4 ++++ 2 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/IDLE/2018-02-04-19-45-46.bpo-5594.Ydp0aG.rst diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index af153593e3c46d..97f4ec51bdaa67 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -263,10 +263,10 @@ Options menu (Shell and Editor) Configure IDLE Open a configuration dialog and change preferences for the following: fonts, indentation, keybindings, text color themes, startup windows and - size, additional help sources, and extensions (see below). On OS X, - open the configuration dialog by selecting Preferences in the application - menu. To use a new built-in color theme (IDLE Dark) with older IDLEs, - save it as a new custom theme. + size, shell startup code, editor template code, additional help sources, + and extensions (see below). On OS X, open the configuration dialog by + selecting Preferences in the application menu. To use a new built-in color + theme (IDLE Dark) with older IDLEs, save it as a new custom theme. Non-default user settings are saved in a .idlerc directory in the user's home directory. Problems caused by bad user configuration files are solved @@ -554,6 +554,22 @@ If there are arguments: ``sys.argv`` reflects the arguments passed to IDLE itself. +Optional Startup Code Execution +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In addition to the ``-``, ``-s``, ``-c``, and ``-r`` command line options for +executing Python code upon startup, code can be entered using the IDLE +Configuration Dialog under the ``Startup`` tab. The code entered here will +be executed if the appropriate check box is selected for running on +startup and if command line options ``-``, ``-c``, and ``-r`` were not used. +Those options take precedence and preclude the configuration code from running. +However, the configuration-level code will be executed before the ``-s`` +option file is run. + +Note that the configuration code isn't checked for errors. If it can't be +executed, then the shell may show errors upon starting. + + Startup failure ^^^^^^^^^^^^^^^ diff --git a/Misc/NEWS.d/next/IDLE/2018-02-04-19-45-46.bpo-5594.Ydp0aG.rst b/Misc/NEWS.d/next/IDLE/2018-02-04-19-45-46.bpo-5594.Ydp0aG.rst new file mode 100644 index 00000000000000..981f33cbb394ed --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-02-04-19-45-46.bpo-5594.Ydp0aG.rst @@ -0,0 +1,4 @@ +Add new Startup tab to configuration and move the current startup-related +items from the General tab to this tab. On the Startup tab, add a new +widget for code that should be run at shell startup/restart and a second new +widget for template code to copy to new, blank editors. From 009e30c2ce1fbb4aa0734643ef3d8a8416c45b9d Mon Sep 17 00:00:00 2001 From: Cheryl Sabella Date: Fri, 9 Feb 2018 09:05:19 -0500 Subject: [PATCH 6/6] Name changes and change newline in test --- Lib/idlelib/configdialog.py | 6 +++--- Lib/idlelib/idle_test/test_configdialog.py | 11 +++++------ Lib/idlelib/pyshell.py | 16 ++++++++-------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 53be53a326ed97..56d9a40b17ceb1 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -109,13 +109,13 @@ def create_widgets(self): self.fontpage = FontPage(note, self.highpage) self.keyspage = KeysPage(note) self.genpage = GenPage(note) - self.startuppage = StartupPage(note) + self.startpage = StartPage(note) self.extpage = self.create_page_extensions() note.add(self.fontpage, text='Fonts/Tabs') note.add(self.highpage, text='Highlights') note.add(self.keyspage, text=' Keys ') note.add(self.genpage, text=' General ') - note.add(self.startuppage, text=' Startup ') + note.add(self.startpage, text=' Startup ') note.add(self.extpage, text='Extensions') note.enable_traversal() note.pack(side=TOP, expand=TRUE, fill=BOTH) @@ -2037,7 +2037,7 @@ def update_help_changes(self): ';'.join(self.user_helplist[num-1][:2])) -class StartupPage(Frame): +class StartPage(Frame): def __init__(self, master): super().__init__(master) diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 8552bf77df9e01..df4a7f0ec826ed 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -3,6 +3,7 @@ Half the class creates dialog, half works with user customizations. Coverage: 95%. """ +import os from idlelib import configdialog from test.support import requires requires('gui') @@ -1281,7 +1282,7 @@ def test_update_help_changes(self): d.update_help_changes = Func() -class StartupPageTest(unittest.TestCase): +class StartPageTest(unittest.TestCase): """Test that startup tab widgets enable users to make changes. Test that widget actions set vars and that var changes add @@ -1289,7 +1290,7 @@ class StartupPageTest(unittest.TestCase): """ @classmethod def setUpClass(cls): - page = cls.page = dialog.startuppage + page = cls.page = dialog.startpage dialog.note.select(page) @classmethod @@ -1362,8 +1363,7 @@ def test_restart_code_on(self): def test_shell_startup_text(self): d = self.page d.shell_startup_text.delete('1.0', 'end') - text = '''import os\n - import re''' + text = f'import os{os.linesep}import re' d.shell_startup_text.insert('end', text) self.assertEqual(mainpage, {'ShellWindow': {'shell-startup-code': text}}) @@ -1375,8 +1375,7 @@ def test_shell_startup_text(self): def test_editor_template_text(self): d = self.page d.editor_template_text.delete('1.0', 'end') - text = '''# This is a comment.\n - # Second line''' + text = f'# This is a comment.{os.linesep}# Second line' d.editor_template_text.insert('end', text) self.assertEqual( mainpage, diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 94cfb86924023c..19425eb4da94f2 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -497,9 +497,9 @@ def restart_subprocess(self, with_cwd=False, filename=''): self.compile.compiler.flags = self.original_compiler_flags if idleConf.GetOption('main', 'ShellWindow', 'restart-code-on', type='bool'): - config_startup_code = idleConf.GetOption( + config_startup = idleConf.GetOption( 'main', 'ShellWindow', 'shell-startup-code') - self.execsource(config_startup_code) + self.execsource(config_startup) self.restarting = False return self.rpcclt @@ -1387,7 +1387,7 @@ def main(): debug = False cmd = None script = None - config_startup_code = None + config_startup = None startup = False try: opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:") @@ -1458,9 +1458,9 @@ def main(): enable_shell = enable_shell or not enable_edit if idleConf.GetOption('main', 'ShellWindow', 'startup-code-on', type='bool'): - config_startup_code = idleConf.GetOption( + config_startup = idleConf.GetOption( 'main', 'ShellWindow', 'shell-startup-code') - sys.argv = ['-c'] + [config_startup_code] + sys.argv = ['-c'] + [config_startup] # Setup root. Don't break user code run in IDLE process. # Don't change environment when testing. @@ -1521,7 +1521,7 @@ def main(): os.environ.get("PYTHONSTARTUP") if filename and os.path.isfile(filename): shell.interp.execfile(filename) - if cmd or script or config_startup_code: + if cmd or script or config_startup: shell.interp.runcommand("""if 1: import sys as _sys _sys.argv = %r @@ -1532,8 +1532,8 @@ def main(): elif script: shell.interp.prepend_syspath(script) shell.interp.execfile(script) - elif config_startup_code: - shell.interp.execsource(config_startup_code) + elif config_startup: + shell.interp.execsource(config_startup) elif shell: # If there is a shell window and no cmd or script in progress, # check for problematic OS X Tk versions and print a warning