From d39a5ec5bbcb953b9f82362aa7fcdbc098694cd9 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 6 Jul 2017 15:58:29 -0400 Subject: [PATCH 01/13] Make dump a function; run unittest in 'if __name__:'. --- Lib/idlelib/config.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 6d683e2b80ac07..563027115254a4 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -766,7 +766,6 @@ def SaveUserCfgFiles(self): idleConf = IdleConf() - _warned = set() def _warn(msg, *key): key = (msg,) + key @@ -779,8 +778,7 @@ def _warn(msg, *key): # TODO Revise test output, write expanded unittest -# -if __name__ == '__main__': +def _dump(): # htest # (not really, but ignore in coverage) from zlib import crc32 line, crc = 0, 0 @@ -790,10 +788,10 @@ def sprint(obj): line += 1 crc = crc32(txt.encode(encoding='utf-8'), crc) print(txt) - #print('***', line, crc, '***') # uncomment for diagnosis + #print('***', line, crc, '***') # Uncomment for diagnosis. def dumpCfg(cfg): - print('\n', cfg, '\n') # has variable '0xnnnnnnnn' addresses + print('\n', cfg, '\n') # Cfg has variable '0xnnnnnnnn' address. for key in sorted(cfg.keys()): sections = cfg[key].sections() sprint(key) @@ -808,3 +806,9 @@ def dumpCfg(cfg): dumpCfg(idleConf.defaultCfg) dumpCfg(idleConf.userCfg) print('\nlines = ', line, ', crc = ', crc, sep='') + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_config', + verbosity=2, exit=False) + #_dump() From 32ae95966f5b7c466b2a34f5eb82673f58155ad5 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 6 Jul 2017 16:34:41 -0400 Subject: [PATCH 02/13] Add class ConfigChanges (changes_class_v4.py on issue). --- Lib/idlelib/config.py | 74 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 563027115254a4..11cc6a5a98ca42 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -777,6 +777,80 @@ def _warn(msg, *key): _warned.add(key) +class ConfigChanges: + """Manage a user's proposed configuration option changes. + + Names used across multiple methods: + page -- one of the 4 top-level dicts representing a + .idlerc/config-x.cfg file. + config_type -- name of a page. + section -- a section within a page/file. + item -- name of a value within a section. + value -- value for the item. + + Methods + add_item: + save_all: Save all the changes to the config file. + + reset: Clear all changes by clearing each page. + set_user_value: Set value *in idleConf* for page, section, item. + """ + def __init__(self): + "Create a page for each configuration file" + self.pages = {} + for config_type in ('main', 'highlight', 'keys', 'extensions'): + self[config_type] = {} + self.pages.add(self[config_type]) + + def add_item(self, config_type, section, item, value): + "Add item/value pair for config_type and section." + page = self[config_type] + value = str(value) # Make sure we use a string. + if section not in page: + page[section] = {} + page[section][item] = value + + def save_all(self): + "Save configuration changes to the user config file." + + idleConf.userCfg['main'].Save() + for config_type in changes: + cfg_type_changed = False + for section in changes[config_type]: + if section == 'HelpFiles': + # This section gets completely replaced. + idleConf.userCfg['main'].remove_section('HelpFiles') + cfg_type_changed = True + for item in changes[config_type][section]: + value = self.changed_items[config_type][section][item] + if self.set_user_value(config_type, section, item, value): + cfg_type_changed = True + if cfg_type_changed: + idleConf.userCfg[config_type].Save() + for config_type in ['keys', 'highlight']: + # Save these even if unchanged! + idleConf.userCfg[config_type].Save() + self.clear() # Clear the changed items dict. + self.save_all_changed_extensions() # Uses a different mechanism. + + def delete_section(self, config_type, section): + """Delete a section from this page. + + Used to delete custom themes and keysets. + """ + if section in self[config_type]: + del self[config_type][section] + + def clear(self): + """Clear all 4 pages. + + Called in save_all after saving to idleConf. + XXX Mark window *title* when there are changes; unmark here. + """ + for page in self.config_types: + page.clear() + + # TODO Revise test output, write expanded unittest def _dump(): # htest # (not really, but ignore in coverage) from zlib import crc32 From a8efe0f2f1303b9c09a83cda8d7026475c1b787e Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 6 Jul 2017 17:07:42 -0400 Subject: [PATCH 03/13] Make sure that filesystem not touched with empty file name. --- Lib/idlelib/config.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 11cc6a5a98ca42..9332e7fcc586fe 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -44,7 +44,7 @@ def __init__(self, cfgFile, cfgDefaults=None): """ cfgFile - string, fully specified configuration file name """ - self.file = cfgFile + self.file = cfgFile # This is currently '' when testing. ConfigParser.__init__(self, defaults=cfgDefaults, strict=False) def Get(self, section, option, type=None, default=None, raw=False): @@ -73,7 +73,8 @@ def GetOptionList(self, section): def Load(self): "Load the configuration file from disk." - self.read(self.file) + if self.file: + self.read(self.file) class IdleUserConfParser(IdleConfParser): """ @@ -130,21 +131,22 @@ def RemoveFile(self): def Save(self): """Update user configuration file. - Remove empty sections. If resulting config isn't empty, write the file - to disk. If config is empty, remove the file from disk if it exists. + If self not empty after removing empty sections, write the file + to disk. Otherwise, remove the file from disk if it exists. """ - if not self.IsEmpty(): - fname = self.file - try: - cfgFile = open(fname, 'w') - except OSError: - os.unlink(fname) - cfgFile = open(fname, 'w') - with cfgFile: - self.write(cfgFile) - else: - self.RemoveFile() + fname = self.file + if self.fname: + if not self.IsEmpty(): + try: + cfgFile = open(fname, 'w') + except OSError: + os.unlink(fname) + cfgFile = open(fname, 'w') + with cfgFile: + self.write(cfgFile) + else: + self.RemoveFile() class IdleConf: """Hold config parsers for all idle config files in singleton instance. From 1a9c0d80624349d9ee43eb9a5dfaee102bc06c0e Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 6 Jul 2017 20:19:59 -0400 Subject: [PATCH 04/13] Test ConfigChanges and modify as needed. --- Lib/idlelib/config.py | 44 +++++++++----- Lib/idlelib/idle_test/test_config.py | 85 +++++++++++++++++++++++++++- 2 files changed, 112 insertions(+), 17 deletions(-) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 9332e7fcc586fe..380db109e6290e 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -136,7 +136,7 @@ def Save(self): """ fname = self.file - if self.fname: + if fname: if not self.IsEmpty(): try: cfgFile = open(fname, 'w') @@ -779,7 +779,7 @@ def _warn(msg, *key): _warned.add(key) -class ConfigChanges: +class ConfigChanges(dict): """Manage a user's proposed configuration option changes. Names used across multiple methods: @@ -799,12 +799,12 @@ class ConfigChanges: """ def __init__(self): "Create a page for each configuration file" - self.pages = {} + self.pages = [] # List of unhashable dicts. for config_type in ('main', 'highlight', 'keys', 'extensions'): self[config_type] = {} - self.pages.add(self[config_type]) + self.pages.append(self[config_type]) - def add_item(self, config_type, section, item, value): + def additem(self, config_type, section, item, value): "Add item/value pair for config_type and section." page = self[config_type] value = str(value) # Make sure we use a string. @@ -812,28 +812,42 @@ def add_item(self, config_type, section, item, value): page[section] = {} page[section][item] = value + + @staticmethod + def set_value(config_type, section, item, value): + """Return True if the configuration value was added or changed. + + Helper for save_all. + """ + if idleConf.defaultCfg[config_type].has_option(section, item): + if idleConf.defaultCfg[config_type].Get(section, item) == value: + # The setting equals a default setting, remove it from user cfg. + return idleConf.userCfg[config_type].RemoveOption(section, item) + # If we got here, set the option. + return idleConf.userCfg[config_type].SetOption(section, item, value) + def save_all(self): "Save configuration changes to the user config file." idleConf.userCfg['main'].Save() - for config_type in changes: + for config_type in self: cfg_type_changed = False - for section in changes[config_type]: - if section == 'HelpFiles': - # This section gets completely replaced. + page = self[config_type] + for section in page: + if section == 'HelpFiles': # Remove it for replacement. idleConf.userCfg['main'].remove_section('HelpFiles') cfg_type_changed = True - for item in changes[config_type][section]: - value = self.changed_items[config_type][section][item] - if self.set_user_value(config_type, section, item, value): + for item, value in page[section].items(): + if self.set_value(config_type, section, item, value): cfg_type_changed = True if cfg_type_changed: idleConf.userCfg[config_type].Save() for config_type in ['keys', 'highlight']: # Save these even if unchanged! idleConf.userCfg[config_type].Save() - self.clear() # Clear the changed items dict. - self.save_all_changed_extensions() # Uses a different mechanism. + self.clear() + # ConfigDialog caller must add the following call + # self.save_all_changed_extensions() # Uses a different mechanism. def delete_section(self, config_type, section): """Delete a section from this page. @@ -849,7 +863,7 @@ def clear(self): Called in save_all after saving to idleConf. XXX Mark window *title* when there are changes; unmark here. """ - for page in self.config_types: + for page in self.pages: page.clear() diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index e678cc6f332670..8c89aab7503e34 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -9,12 +9,13 @@ # Tests should not depend on fortuitous user configurations. # They must not affect actual user .cfg files. -# Replace user parsers with empty parsers that cannot be saved. +# Replace user parsers with empty parsers that cannot be saved +# due to getting '' as the filename when created. idleConf = config.idleConf usercfg = idleConf.userCfg testcfg = {} -usermain = testcfg['main'] = config.IdleUserConfParser('') # filename +usermain = testcfg['main'] = config.IdleUserConfParser('') userhigh = testcfg['highlight'] = config.IdleUserConfParser('') userkeys = testcfg['keys'] = config.IdleUserConfParser('') @@ -136,6 +137,86 @@ def test_user_override_keys(self): userkeys.remove_section('Custom Keys') +class ChangesTest(unittest.TestCase): + + empty = {'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}} + + def load(self): + changes = self.changes + changes.additem('main', 'Msec', 'mitem', 'mval') + changes.additem('highlight', 'Hsec', 'hitem', 'hval') + changes.additem('keys', 'Ksec', 'kitem', 'kval') + return changes + + loaded = {'main': {'Msec': {'mitem': 'mval'}}, + 'highlight': {'Hsec': {'hitem': 'hval'}}, + 'keys': {'Ksec': {'kitem':'kval'}}, + 'extensions': {}} + + def setUp(self): + self.changes = config.ConfigChanges() + + def test_init(self): + self.assertEqual(self.changes, self.empty) + + def test_additem(self): + changes = self.load() + self.assertEqual(changes, self.loaded) + changes.additem('main', 'Msec', 'mitem', 'mval') + self.assertEqual(changes, self.loaded) + + def test_set_value(self): # Static function does not touch changes. + setval = self.changes.set_value + self.assertTrue(setval('main', 'Indent', 'what', '0')) + self.assertFalse(setval('main', 'Indent', 'what', '0')) + self.assertEqual(usermain['Indent']['what'], '0') + + self.assertTrue(setval('main', 'Indent', 'use-spaces', '0')) + self.assertEqual(usermain['Indent']['use-spaces'], '0') + self.assertTrue(setval('main', 'Indent', 'use-spaces', '1')) + self.assertFalse(usermain.has_option('Indent', 'use-spaces')) + usermain.remove_section('Indent') + + def test_save_added(self): + changes = self.load() + changes.save_all() + self.assertEqual(usermain['Msec']['mitem'], 'mval') + self.assertEqual(userhigh['Hsec']['hitem'], 'hval') + self.assertEqual(userkeys['Ksec']['kitem'], 'kval') + usermain.remove_section('Msec') + userhigh.remove_section('Hsec') + userkeys.remove_section('Ksec') + + def test_save_help(self): + changes = self.changes + changes.set_value('main', 'HelpFiles', 'IDLE', 'idledoc') + changes.additem('main', 'HelpFiles', 'ELDI', 'codeldi') + changes.save_all() + self.assertFalse(usermain.has_option('HelpFiles', 'IDLE')) + self.assertTrue(usermain.has_option('HelpFiles', 'ELDI')) + + def test_save_default(self): # Cover 2nd and 3rd false branches. + changes = self.changes + changes.additem('main', 'Indent', 'use-spaces', '1') + # set_value returns False; cfg_type_changed remains False. + + # TODO: test that save_all calls usercfg Saves. + + def test_delete_section(self): + changes = self.load() + changes.delete_section('main', 'fake') # Test no exception. + self.assertEqual(changes, self.loaded) # Test nothing deleted. + for cfgtype, section in (('main', 'Msec'), ('keys', 'Ksec')): + changes.delete_section(cfgtype, section) + with self.assertRaises(KeyError): + changes[cfgtype][section] # Test section gone. + + def test_clear(self): + changes = self.load() + changes.clear() + self.assertEqual(changes, self.empty) + + class WarningTest(unittest.TestCase): def test_warn(self): From 3f381a371167964aab87b32bfd347a3c0144520d Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 6 Jul 2017 21:37:55 -0400 Subject: [PATCH 05/13] Tweaks to config and test while editing configdialog. --- Lib/idlelib/config.py | 7 +++++-- Lib/idlelib/idle_test/test_config.py | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 380db109e6290e..4f77188a0d4fd8 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -812,7 +812,6 @@ def additem(self, config_type, section, item, value): page[section] = {} page[section][item] = value - @staticmethod def set_value(config_type, section, item, value): """Return True if the configuration value was added or changed. @@ -850,12 +849,16 @@ def save_all(self): # self.save_all_changed_extensions() # Uses a different mechanism. def delete_section(self, config_type, section): - """Delete a section from this page. + """Delete a section from self, userCfg, and file. Used to delete custom themes and keysets. """ if section in self[config_type]: del self[config_type][section] + configpage = idleConf.userCfg[config_type] + configpage.remove_section(section) + configpage.Save() + def clear(self): """Clear all 4 pages. diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index 8c89aab7503e34..0367fa8e6417db 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -210,6 +210,7 @@ def test_delete_section(self): changes.delete_section(cfgtype, section) with self.assertRaises(KeyError): changes[cfgtype][section] # Test section gone. + # TODO Test change to userkeys and maybe save call. def test_clear(self): changes = self.load() From f22fc6e9df6ea21727aa2b47b8a6a4507f6e5531 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 6 Jul 2017 21:45:58 -0400 Subject: [PATCH 06/13] Initial revisions of configdialog as in tracker msg297804. --- Lib/idlelib/configdialog.py | 206 ++++++++++++++++++------------------ 1 file changed, 101 insertions(+), 105 deletions(-) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 84092275183685..7df5a15e25c13f 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -20,7 +20,7 @@ import tkinter.font as tkFont import tkinter.messagebox as tkMessageBox -from idlelib.config import idleConf +from idlelib.config import idleConf, ConfigChanges from idlelib.config_key import GetKeysDialog from idlelib.dynoption import DynOptionMenu from idlelib import macosx @@ -28,6 +28,8 @@ from idlelib.tabbedpages import TabbedPageSet from idlelib.textview import view_text +changes = ConfigChanges() + class ConfigDialog(Toplevel): """Config dialog for IDLE. @@ -71,7 +73,6 @@ def __init__(self, parent, title='', _htest=False, _utest=False): 'Shell Stdout Text': ('stdout', '12'), 'Shell Stderr Text': ('stderr', '13'), } - self.reset_changed_items() # Initialize changed_items dict. self.create_widgets() self.resizable(height=FALSE, width=FALSE) self.transient(parent) @@ -559,16 +560,16 @@ def var_changed_font(self, *params): overriding the default font, we need to write out everything. """ value = self.font_name.get() - self.add_changed_item('main', 'EditorWindow', 'font', value) + changes.additem('main', 'EditorWindow', 'font', value) value = self.font_size.get() - self.add_changed_item('main', 'EditorWindow', 'font-size', value) + changes.additem('main', 'EditorWindow', 'font-size', value) value = self.font_bold.get() - self.add_changed_item('main', 'EditorWindow', 'font-bold', value) + changes.additem('main', 'EditorWindow', 'font-bold', value) def var_changed_space_num(self, *params): "Store change to indentation size." value = self.space_num.get() - self.add_changed_item('main', 'Indent', 'num-spaces', value) + changes.additem('main', 'Indent', 'num-spaces', value) def var_changed_colour(self, *params): "Process change to color choice." @@ -584,13 +585,13 @@ def var_changed_builtin_theme(self, *params): value = self.builtin_theme.get() if value not in old_themes: if idleConf.GetOption('main', 'Theme', 'name') not in old_themes: - self.add_changed_item('main', 'Theme', 'name', old_themes[0]) - self.add_changed_item('main', 'Theme', 'name2', value) + changes.additem('main', 'Theme', 'name', old_themes[0]) + changes.additem('main', 'Theme', 'name2', value) self.new_custom_theme.config(text='New theme, see Help', fg='#500000') else: - self.add_changed_item('main', 'Theme', 'name', value) - self.add_changed_item('main', 'Theme', 'name2', '') + changes.additem('main', 'Theme', 'name', value) + changes.additem('main', 'Theme', 'name2', '') self.new_custom_theme.config(text='', fg='black') self.paint_theme_sample() @@ -602,7 +603,7 @@ def var_changed_custom_theme(self, *params): """ value = self.custom_theme.get() if value != '- no custom themes -': - self.add_changed_item('main', 'Theme', 'name', value) + changes.additem('main', 'Theme', 'name', value) self.paint_theme_sample() def var_changed_is_builtin_theme(self, *params): @@ -612,7 +613,7 @@ def var_changed_is_builtin_theme(self, *params): selected theme type. """ value = self.is_builtin_theme.get() - self.add_changed_item('main', 'Theme', 'default', value) + changes.additem('main', 'Theme', 'default', value) if value: self.var_changed_builtin_theme() else: @@ -628,11 +629,11 @@ def var_changed_keybinding(self, *params): key_set = self.custom_keys.get() event = self.list_bindings.get(ANCHOR).split()[0] if idleConf.IsCoreBinding(event): - self.add_changed_item('keys', key_set, event, value) + changes.additem('keys', key_set, event, value) else: # Event is an extension binding. ext_name = idleConf.GetExtnNameForEvent(event) ext_keybind_section = ext_name + '_cfgBindings' - self.add_changed_item('extensions', ext_keybind_section, event, value) + changes.additem('extensions', ext_keybind_section, event, value) def var_changed_builtin_keys(self, *params): "Process selection of builtin key set." @@ -645,13 +646,13 @@ def var_changed_builtin_keys(self, *params): value = self.builtin_keys.get() if value not in old_keys: if idleConf.GetOption('main', 'Keys', 'name') not in old_keys: - self.add_changed_item('main', 'Keys', 'name', old_keys[0]) - self.add_changed_item('main', 'Keys', 'name2', value) + changes.additem('main', 'Keys', 'name', old_keys[0]) + changes.additem('main', 'Keys', 'name2', value) self.new_custom_keys.config(text='New key set, see Help', fg='#500000') else: - self.add_changed_item('main', 'Keys', 'name', value) - self.add_changed_item('main', 'Keys', 'name2', '') + changes.additem('main', 'Keys', 'name', value) + changes.additem('main', 'Keys', 'name2', '') self.new_custom_keys.config(text='', fg='black') self.load_keys_list(value) @@ -659,13 +660,13 @@ def var_changed_custom_keys(self, *params): "Process selection of custom key set." value = self.custom_keys.get() if value != '- no custom keys -': - self.add_changed_item('main', 'Keys', 'name', value) + changes.additem('main', 'Keys', 'name', value) self.load_keys_list(value) def var_changed_are_keys_builtin(self, *params): "Process toggle between builtin key set and custom key set." value = self.are_keys_builtin.get() - self.add_changed_item('main', 'Keys', 'default', value) + changes.additem('main', 'Keys', 'default', value) if value: self.var_changed_builtin_keys() else: @@ -674,47 +675,47 @@ def var_changed_are_keys_builtin(self, *params): def var_changed_win_width(self, *params): "Store change to window width." value = self.win_width.get() - self.add_changed_item('main', 'EditorWindow', 'width', value) + changes.additem('main', 'EditorWindow', 'width', value) def var_changed_win_height(self, *params): "Store change to window height." value = self.win_height.get() - self.add_changed_item('main', 'EditorWindow', 'height', value) + changes.additem('main', 'EditorWindow', 'height', value) def var_changed_startup_edit(self, *params): "Store change to toggle for starting IDLE in the editor or shell." value = self.startup_edit.get() - self.add_changed_item('main', 'General', 'editor-on-startup', value) + changes.additem('main', 'General', 'editor-on-startup', value) def var_changed_autosave(self, *params): "Store change to autosave." value = self.autosave.get() - self.add_changed_item('main', 'General', 'autosave', value) + changes.additem('main', 'General', 'autosave', value) def var_changed_encoding(self, *params): "Store change to encoding." value = self.encoding.get() - self.add_changed_item('main', 'EditorWindow', 'encoding', value) - - def reset_changed_items(self): - """Reset dictionary containing the items changed on each tab. - - When any config item is changed in this dialog, an entry - should be made in the relevant section (config type) of this - dictionary. The key should be the config file section name and the - value a dictionary, whose key:value pairs are item=value pairs for - that config file section. - """ - self.changed_items = {'main':{}, 'highlight':{}, 'keys':{}, - 'extensions':{}} - - def add_changed_item(self, typ, section, item, value): - "Add item/value pair to changed items dictionary for typ and section." - value = str(value) # Make sure we use a string. - if section not in self.changed_items[typ]: - self.changed_items[typ][section] = {} - self.changed_items[typ][section][item] = value - + changes.additem('main', 'EditorWindow', 'encoding', value) + +## def reset_changed_items(self): +## """Reset dictionary containing the items changed on each tab. +## +## When any config item is changed in this dialog, an entry +## should be made in the relevant section (config type) of this +## dictionary. The key should be the config file section name and the +## value a dictionary, whose key:value pairs are item=value pairs for +## that config file section. +## """ +## changes = {'main':{}, 'highlight':{}, 'keys':{}, +## 'extensions':{}} +## +## def add_changed_item(self, typ, section, item, value): +## "Add item/value pair to changed items dictionary for typ and section." +## value = str(value) # Make sure we use a string. +## if section not in changes[typ]: +## changes[typ][section] = {} +## changes[typ][section][item] = value +## def GetDefaultItems(self): "Return dictionary of default configuration settings." d_items={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}} @@ -769,8 +770,8 @@ def get_new_keys(self): else: current_key_set_name = self.custom_keys.get() current_bindings = idleConf.GetCurrentKeySet() - if current_key_set_name in self.changed_items['keys']: # unsaved changes - key_set_changes = self.changed_items['keys'][current_key_set_name] + if current_key_set_name in changes['keys']: # unsaved changes + key_set_changes = changes['keys'][current_key_set_name] for event in key_set_changes: current_bindings[event] = key_set_changes[event].split() current_key_sequences = list(current_bindings.values()) @@ -832,8 +833,8 @@ def create_new_key_set(self, new_key_set_name): binding = ' '.join(prev_keys[event]) new_keys[event_name] = binding # Handle any unsaved changes to prev key set. - if prev_key_set_name in self.changed_items['keys']: - key_set_changes = self.changed_items['keys'][prev_key_set_name] + if prev_key_set_name in changes['keys']: + key_set_changes = changes['keys'][prev_key_set_name] for event in key_set_changes: new_keys[event] = key_set_changes[event] # Save the new key set. @@ -863,10 +864,10 @@ def load_keys_list(self, keyset_name): for bind_name in bind_names: key = ' '.join(keyset[bind_name]) bind_name = bind_name[2:-2] # Trim off the angle brackets. - if keyset_name in self.changed_items['keys']: + if keyset_name in changes['keys']: # Handle any unsaved changes to this key set. - if bind_name in self.changed_items['keys'][keyset_name]: - key = self.changed_items['keys'][keyset_name][bind_name] + if bind_name in changes['keys'][keyset_name]: + key = changes['keys'][keyset_name][bind_name] self.list_bindings.insert(END, bind_name+' - '+key) if reselect: self.list_bindings.see(list_index) @@ -886,12 +887,8 @@ def delete_custom_keys(self): 'Delete Key Set', delmsg % keyset_name, parent=self): return self.deactivate_current_config() - # Remove key set from config. - idleConf.userCfg['keys'].remove_section(keyset_name) - if keyset_name in self.changed_items['keys']: - del(self.changed_items['keys'][keyset_name]) - # Write changes. - idleConf.userCfg['keys'].Save() + # Remove key set from changes, config, and file. + changes.remove(keyset_name) # Reload user key set list. item_list = idleConf.GetSectionList('user', 'keys') item_list.sort() @@ -906,7 +903,8 @@ def delete_custom_keys(self): self.builtin_keys.set(idleConf.defaultCfg['main'].Get('Keys', 'name') or idleConf.default_keys()) # User can't back out of these changes, they must be applied now. - self.save_all_changed_configs() + changes.save_all() + self.save_all_changed_extensions() self.activate_config_changes() self.set_keys_type() @@ -923,12 +921,8 @@ def delete_custom_theme(self): 'Delete Theme', delmsg % theme_name, parent=self): return self.deactivate_current_config() - # Remove theme from config. - idleConf.userCfg['highlight'].remove_section(theme_name) - if theme_name in self.changed_items['highlight']: - del(self.changed_items['highlight'][theme_name]) - # Write changes. - idleConf.userCfg['highlight'].Save() + # Remove theme from changes, config, and file. + changes.delete_section('highlight') # Reload user theme list. item_list = idleConf.GetSectionList('user', 'highlight') item_list.sort() @@ -941,7 +935,8 @@ def delete_custom_theme(self): self.is_builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'default')) self.builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) # User can't back out of these changes, they must be applied now. - self.save_all_changed_configs() + changes.save_all() + self.save_all_changed_extensions() self.activate_config_changes() self.set_theme_type() @@ -979,7 +974,7 @@ def on_new_colour_set(self): self.text_highlight_sample.tag_config(sample_element, **{plane:new_colour}) theme = self.custom_theme.get() theme_element = sample_element + '-' + plane - self.add_changed_item('highlight', theme, theme_element, new_colour) + changes.additem('highlight', theme, theme_element, new_colour) def get_new_theme_name(self, message): "Return name of new theme from query popup." @@ -1010,8 +1005,8 @@ def create_new_theme(self, new_theme_name): theme_name = self.custom_theme.get() new_theme = idleConf.GetThemeDict(theme_type, theme_name) # Apply any of the old theme's unsaved changes to the new theme. - if theme_name in self.changed_items['highlight']: - theme_changes = self.changed_items['highlight'][theme_name] + if theme_name in changes['highlight']: + theme_changes = changes['highlight'][theme_name] for element in theme_changes: new_theme[element] = theme_changes[element] # Save the new theme. @@ -1078,8 +1073,8 @@ def paint_theme_sample(self): colours['background'] = idleConf.GetHighlight( theme, 'normal', fgBg='bg') # Handle any unsaved changes to this theme. - if theme in self.changed_items['highlight']: - theme_dict = self.changed_items['highlight'][theme] + if theme in changes['highlight']: + theme_dict = changes['highlight'][theme] if element + '-foreground' in theme_dict: colours['foreground'] = theme_dict[element + '-foreground'] if element + '-background' in theme_dict: @@ -1150,10 +1145,10 @@ def helplist_item_remove(self): self.set_helplist_button_states() def update_user_help_changed_items(self): - "Clear and rebuild the HelpFiles section in self.changed_items" - self.changed_items['main']['HelpFiles'] = {} + "Clear and rebuild the HelpFiles section in changes" + changes['main']['HelpFiles'] = {} for num in range(1, len(self.user_helplist) + 1): - self.add_changed_item( + changes.additem( 'main', 'HelpFiles', str(num), ';'.join(self.user_helplist[num-1][:2])) @@ -1317,36 +1312,36 @@ def save_new_theme(self, theme_name, theme): value = theme[element] idleConf.userCfg['highlight'].SetOption(theme_name, element, value) - def set_user_value(self, config_type, section, item, value): - "Return True if the configuration value was added or changed." - if idleConf.defaultCfg[config_type].has_option(section, item): - if idleConf.defaultCfg[config_type].Get(section, item) == value: - # The setting equals a default setting, remove it from user cfg. - return idleConf.userCfg[config_type].RemoveOption(section, item) - # If we got here, set the option. - return idleConf.userCfg[config_type].SetOption(section, item, value) - - def save_all_changed_configs(self): - "Save all configuration changes to the user config file." - idleConf.userCfg['main'].Save() - for config_type in self.changed_items: - cfg_type_changed = False - for section in self.changed_items[config_type]: - if section == 'HelpFiles': - # This section gets completely replaced. - idleConf.userCfg['main'].remove_section('HelpFiles') - cfg_type_changed = True - for item in self.changed_items[config_type][section]: - value = self.changed_items[config_type][section][item] - if self.set_user_value(config_type, section, item, value): - cfg_type_changed = True - if cfg_type_changed: - idleConf.userCfg[config_type].Save() - for config_type in ['keys', 'highlight']: - # Save these even if unchanged! - idleConf.userCfg[config_type].Save() - self.reset_changed_items() # Clear the changed items dict. - self.save_all_changed_extensions() # Uses a different mechanism. +## def set_user_value(self, config_type, section, item, value): +## "Return True if the configuration value was added or changed." +## if idleConf.defaultCfg[config_type].has_option(section, item): +## if idleConf.defaultCfg[config_type].Get(section, item) == value: +## # The setting equals a default setting, remove it from user cfg. +## return idleConf.userCfg[config_type].RemoveOption(section, item) +## # If we got here, set the option. +## return idleConf.userCfg[config_type].SetOption(section, item, value) +## +## def save_all_changed_configs(self): +## "Save all configuration changes to the user config file." +## idleConf.userCfg['main'].Save() +## for config_type in changes: +## cfg_type_changed = False +## for section in changes[config_type]: +## if section == 'HelpFiles': +## # This section gets completely replaced. +## idleConf.userCfg['main'].remove_section('HelpFiles') +## cfg_type_changed = True +## for item in changes[config_type][section]: +## value = changes[config_type][section][item] +## if self.set_user_value(config_type, section, item, value): +## cfg_type_changed = True +## if cfg_type_changed: +## idleConf.userCfg[config_type].Save() +## for config_type in ['keys', 'highlight']: +## # Save these even if unchanged! +## idleConf.userCfg[config_type].Save() +## self.reset_changed_items() # Clear the changed items dict. +## self.save_all_changed_extensions() # Uses a different mechanism. def deactivate_current_config(self): "Remove current key bindings." @@ -1378,7 +1373,8 @@ def ok(self): def apply(self): "Apply config changes and leave dialog open." self.deactivate_current_config() - self.save_all_changed_configs() + changes.save_all() + self.save_all_changed_extensions() self.activate_config_changes() def help(self): From 6e6f4ee5277f19f9fb7c401f916558291abd4bc9 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 6 Jul 2017 22:16:47 -0400 Subject: [PATCH 07/13] Revise configdialog tests to match other changes. All pass. --- Lib/idlelib/idle_test/test_configdialog.py | 57 ++++++++++------------ 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 4946f02306a786..6ff884a29a7a3d 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -3,7 +3,7 @@ Half the class creates dialog, half works with user customizations. Coverage: 46% just by creating dialog, 56% with current tests. """ -from idlelib.configdialog import ConfigDialog, idleConf # test import +from idlelib.configdialog import ConfigDialog, idleConf, changes from test.support import requires requires('gui') from tkinter import Tk @@ -21,18 +21,14 @@ 'extensions': config.IdleUserConfParser(''), } -# ConfigDialog.changed_items is a 3-level hierarchical dictionary of -# pending changes that mirrors the multilevel user config dict. -# For testing, record args in a list for comparison with expected. -changes = [] root = None configure = None +mainpage = changes['main'] +highpage = changes['highlight'] +keyspage = changes['keys'] - -class TestDialog(ConfigDialog): - def add_changed_item(self, *args): - changes.append(args) - +class TestDialog(ConfigDialog): pass # Delete? + def setUpModule(): global root, configure @@ -44,7 +40,7 @@ def setUpModule(): def tearDownModule(): global root, configure - idleConf.userCfg = testcfg + idleConf.userCfg = usercfg configure.remove_var_callbacks() del configure root.update_idletasks() @@ -63,31 +59,28 @@ def test_font(self): default_size = str(default_font[1]) default_bold = default_font[2] == 'bold' configure.font_name.set('Test Font') - expected = [ - ('main', 'EditorWindow', 'font', 'Test Font'), - ('main', 'EditorWindow', 'font-size', default_size), - ('main', 'EditorWindow', 'font-bold', default_bold)] - self.assertEqual(changes, expected) + expected = {'EditorWindow': {'font': 'Test Font', + 'font-size': default_size, + 'font-bold': str(default_bold)}} + self.assertEqual(mainpage, expected) changes.clear() configure.font_size.set(20) - expected = [ - ('main', 'EditorWindow', 'font', 'Test Font'), - ('main', 'EditorWindow', 'font-size', '20'), - ('main', 'EditorWindow', 'font-bold', default_bold)] - self.assertEqual(changes, expected) + expected = {'EditorWindow': {'font': 'Test Font', + 'font-size': '20', + 'font-bold': str(default_bold)}} + self.assertEqual(mainpage, expected) changes.clear() configure.font_bold.set(not default_bold) - expected = [ - ('main', 'EditorWindow', 'font', 'Test Font'), - ('main', 'EditorWindow', 'font-size', '20'), - ('main', 'EditorWindow', 'font-bold', not default_bold)] - self.assertEqual(changes, expected) + expected = {'EditorWindow': {'font': 'Test Font', + 'font-size': '20', + 'font-bold': str(not default_bold)}} + self.assertEqual(mainpage, expected) #def test_sample(self): pass # TODO def test_tabspace(self): configure.space_num.set(6) - self.assertEqual(changes, [('main', 'Indent', 'num-spaces', 6)]) + self.assertEqual(mainpage, {'Indent': {'num-spaces': '6'}}) class HighlightTest(unittest.TestCase): @@ -111,19 +104,19 @@ def setUp(self): def test_startup(self): configure.radio_startup_edit.invoke() - self.assertEqual(changes, - [('main', 'General', 'editor-on-startup', 1)]) + self.assertEqual(mainpage, + {'General': {'editor-on-startup': '1'}}) def test_autosave(self): configure.radio_save_auto.invoke() - self.assertEqual(changes, [('main', 'General', 'autosave', 1)]) + self.assertEqual(mainpage, {'General': {'autosave': '1'}}) def test_editor_size(self): configure.entry_win_height.insert(0, '1') - self.assertEqual(changes, [('main', 'EditorWindow', 'height', '140')]) + self.assertEqual(mainpage, {'EditorWindow': {'height': '140'}}) changes.clear() configure.entry_win_width.insert(0, '1') - self.assertEqual(changes, [('main', 'EditorWindow', 'width', '180')]) + self.assertEqual(mainpage, {'EditorWindow': {'width': '180'}}) #def test_help_sources(self): pass # TODO From e3e5d5ea1ba2359a74cfb470600066550da56e8c Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 6 Jul 2017 22:24:02 -0400 Subject: [PATCH 08/13] Remove configdialog functions reproduced in new class. --- Lib/idlelib/configdialog.py | 50 ------------------------------------- 1 file changed, 50 deletions(-) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 7df5a15e25c13f..974e93f829e4a2 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -697,25 +697,6 @@ def var_changed_encoding(self, *params): value = self.encoding.get() changes.additem('main', 'EditorWindow', 'encoding', value) -## def reset_changed_items(self): -## """Reset dictionary containing the items changed on each tab. -## -## When any config item is changed in this dialog, an entry -## should be made in the relevant section (config type) of this -## dictionary. The key should be the config file section name and the -## value a dictionary, whose key:value pairs are item=value pairs for -## that config file section. -## """ -## changes = {'main':{}, 'highlight':{}, 'keys':{}, -## 'extensions':{}} -## -## def add_changed_item(self, typ, section, item, value): -## "Add item/value pair to changed items dictionary for typ and section." -## value = str(value) # Make sure we use a string. -## if section not in changes[typ]: -## changes[typ][section] = {} -## changes[typ][section][item] = value -## def GetDefaultItems(self): "Return dictionary of default configuration settings." d_items={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}} @@ -1312,37 +1293,6 @@ def save_new_theme(self, theme_name, theme): value = theme[element] idleConf.userCfg['highlight'].SetOption(theme_name, element, value) -## def set_user_value(self, config_type, section, item, value): -## "Return True if the configuration value was added or changed." -## if idleConf.defaultCfg[config_type].has_option(section, item): -## if idleConf.defaultCfg[config_type].Get(section, item) == value: -## # The setting equals a default setting, remove it from user cfg. -## return idleConf.userCfg[config_type].RemoveOption(section, item) -## # If we got here, set the option. -## return idleConf.userCfg[config_type].SetOption(section, item, value) -## -## def save_all_changed_configs(self): -## "Save all configuration changes to the user config file." -## idleConf.userCfg['main'].Save() -## for config_type in changes: -## cfg_type_changed = False -## for section in changes[config_type]: -## if section == 'HelpFiles': -## # This section gets completely replaced. -## idleConf.userCfg['main'].remove_section('HelpFiles') -## cfg_type_changed = True -## for item in changes[config_type][section]: -## value = changes[config_type][section][item] -## if self.set_user_value(config_type, section, item, value): -## cfg_type_changed = True -## if cfg_type_changed: -## idleConf.userCfg[config_type].Save() -## for config_type in ['keys', 'highlight']: -## # Save these even if unchanged! -## idleConf.userCfg[config_type].Save() -## self.reset_changed_items() # Clear the changed items dict. -## self.save_all_changed_extensions() # Uses a different mechanism. - def deactivate_current_config(self): "Remove current key bindings." # Before a config is saved, some cleanup of current From 10c868131bccbb5c0a1f3f6648c01a1690a80ce2 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 6 Jul 2017 23:27:10 -0400 Subject: [PATCH 09/13] Whitespace --- Lib/idlelib/config.py | 2 +- Lib/idlelib/idle_test/test_config.py | 8 ++++---- Lib/idlelib/idle_test/test_configdialog.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 4f77188a0d4fd8..bd5ac89f52f6e4 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -816,7 +816,7 @@ def additem(self, config_type, section, item, value): def set_value(config_type, section, item, value): """Return True if the configuration value was added or changed. - Helper for save_all. + Helper for save_all. """ if idleConf.defaultCfg[config_type].has_option(section, item): if idleConf.defaultCfg[config_type].Get(section, item) == value: diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index 0367fa8e6417db..eca6c2a8d723db 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -154,7 +154,7 @@ def load(self): 'extensions': {}} def setUp(self): - self.changes = config.ConfigChanges() + self.changes = config.ConfigChanges() def test_init(self): self.assertEqual(self.changes, self.empty) @@ -207,16 +207,16 @@ def test_delete_section(self): changes.delete_section('main', 'fake') # Test no exception. self.assertEqual(changes, self.loaded) # Test nothing deleted. for cfgtype, section in (('main', 'Msec'), ('keys', 'Ksec')): - changes.delete_section(cfgtype, section) + changes.delete_section(cfgtype, section) with self.assertRaises(KeyError): changes[cfgtype][section] # Test section gone. # TODO Test change to userkeys and maybe save call. - + def test_clear(self): changes = self.load() changes.clear() self.assertEqual(changes, self.empty) - + class WarningTest(unittest.TestCase): diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 6ff884a29a7a3d..26b045d353550b 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -28,7 +28,7 @@ keyspage = changes['keys'] class TestDialog(ConfigDialog): pass # Delete? - + def setUpModule(): global root, configure From 6d2e6dde2d98ef2431f45c79a31abfa91f470bde Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 7 Jul 2017 01:03:14 -0400 Subject: [PATCH 10/13] Changes inspired by Louie's comments. --- Lib/idlelib/config.py | 21 ++++++------ Lib/idlelib/configdialog.py | 50 ++++++++++++++-------------- Lib/idlelib/idle_test/test_config.py | 30 ++++++++--------- 3 files changed, 50 insertions(+), 51 deletions(-) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index bd5ac89f52f6e4..974111ca65549b 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -787,15 +787,15 @@ class ConfigChanges(dict): .idlerc/config-x.cfg file. config_type -- name of a page. section -- a section within a page/file. - item -- name of a value within a section. - value -- value for the item. + option -- name of an option within a section. + value -- value for the option. Methods - add_item: - save_all: Save all the changes to the config file. - - reset: Clear all changes by clearing each page. - set_user_value: Set value *in idleConf* for page, section, item. + add_option: Add option and value to changes. + save_option: Save option and value to config parser. + save_all: Save all the changes to the config parser and file. + delete_section: Delete section if it exists. + clear: Clear all changes by clearing each page. """ def __init__(self): "Create a page for each configuration file" @@ -804,7 +804,7 @@ def __init__(self): self[config_type] = {} self.pages.append(self[config_type]) - def additem(self, config_type, section, item, value): + def add_option(self, config_type, section, item, value): "Add item/value pair for config_type and section." page = self[config_type] value = str(value) # Make sure we use a string. @@ -813,7 +813,7 @@ def additem(self, config_type, section, item, value): page[section][item] = value @staticmethod - def set_value(config_type, section, item, value): + def save_option(config_type, section, item, value): """Return True if the configuration value was added or changed. Helper for save_all. @@ -837,7 +837,7 @@ def save_all(self): idleConf.userCfg['main'].remove_section('HelpFiles') cfg_type_changed = True for item, value in page[section].items(): - if self.set_value(config_type, section, item, value): + if self.save_option(config_type, section, item, value): cfg_type_changed = True if cfg_type_changed: idleConf.userCfg[config_type].Save() @@ -859,7 +859,6 @@ def delete_section(self, config_type, section): configpage.remove_section(section) configpage.Save() - def clear(self): """Clear all 4 pages. diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 974e93f829e4a2..ef3570cd194502 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -560,16 +560,16 @@ def var_changed_font(self, *params): overriding the default font, we need to write out everything. """ value = self.font_name.get() - changes.additem('main', 'EditorWindow', 'font', value) + changes.add_option('main', 'EditorWindow', 'font', value) value = self.font_size.get() - changes.additem('main', 'EditorWindow', 'font-size', value) + changes.add_option('main', 'EditorWindow', 'font-size', value) value = self.font_bold.get() - changes.additem('main', 'EditorWindow', 'font-bold', value) + changes.add_option('main', 'EditorWindow', 'font-bold', value) def var_changed_space_num(self, *params): "Store change to indentation size." value = self.space_num.get() - changes.additem('main', 'Indent', 'num-spaces', value) + changes.add_option('main', 'Indent', 'num-spaces', value) def var_changed_colour(self, *params): "Process change to color choice." @@ -585,13 +585,13 @@ def var_changed_builtin_theme(self, *params): value = self.builtin_theme.get() if value not in old_themes: if idleConf.GetOption('main', 'Theme', 'name') not in old_themes: - changes.additem('main', 'Theme', 'name', old_themes[0]) - changes.additem('main', 'Theme', 'name2', value) + changes.add_option('main', 'Theme', 'name', old_themes[0]) + changes.add_option('main', 'Theme', 'name2', value) self.new_custom_theme.config(text='New theme, see Help', fg='#500000') else: - changes.additem('main', 'Theme', 'name', value) - changes.additem('main', 'Theme', 'name2', '') + changes.add_option('main', 'Theme', 'name', value) + changes.add_option('main', 'Theme', 'name2', '') self.new_custom_theme.config(text='', fg='black') self.paint_theme_sample() @@ -603,7 +603,7 @@ def var_changed_custom_theme(self, *params): """ value = self.custom_theme.get() if value != '- no custom themes -': - changes.additem('main', 'Theme', 'name', value) + changes.add_option('main', 'Theme', 'name', value) self.paint_theme_sample() def var_changed_is_builtin_theme(self, *params): @@ -613,7 +613,7 @@ def var_changed_is_builtin_theme(self, *params): selected theme type. """ value = self.is_builtin_theme.get() - changes.additem('main', 'Theme', 'default', value) + changes.add_option('main', 'Theme', 'default', value) if value: self.var_changed_builtin_theme() else: @@ -629,11 +629,11 @@ def var_changed_keybinding(self, *params): key_set = self.custom_keys.get() event = self.list_bindings.get(ANCHOR).split()[0] if idleConf.IsCoreBinding(event): - changes.additem('keys', key_set, event, value) + changes.add_option('keys', key_set, event, value) else: # Event is an extension binding. ext_name = idleConf.GetExtnNameForEvent(event) ext_keybind_section = ext_name + '_cfgBindings' - changes.additem('extensions', ext_keybind_section, event, value) + changes.add_option('extensions', ext_keybind_section, event, value) def var_changed_builtin_keys(self, *params): "Process selection of builtin key set." @@ -646,13 +646,13 @@ def var_changed_builtin_keys(self, *params): value = self.builtin_keys.get() if value not in old_keys: if idleConf.GetOption('main', 'Keys', 'name') not in old_keys: - changes.additem('main', 'Keys', 'name', old_keys[0]) - changes.additem('main', 'Keys', 'name2', value) + changes.add_option('main', 'Keys', 'name', old_keys[0]) + changes.add_option('main', 'Keys', 'name2', value) self.new_custom_keys.config(text='New key set, see Help', fg='#500000') else: - changes.additem('main', 'Keys', 'name', value) - changes.additem('main', 'Keys', 'name2', '') + changes.add_option('main', 'Keys', 'name', value) + changes.add_option('main', 'Keys', 'name2', '') self.new_custom_keys.config(text='', fg='black') self.load_keys_list(value) @@ -660,13 +660,13 @@ def var_changed_custom_keys(self, *params): "Process selection of custom key set." value = self.custom_keys.get() if value != '- no custom keys -': - changes.additem('main', 'Keys', 'name', value) + changes.add_option('main', 'Keys', 'name', value) self.load_keys_list(value) def var_changed_are_keys_builtin(self, *params): "Process toggle between builtin key set and custom key set." value = self.are_keys_builtin.get() - changes.additem('main', 'Keys', 'default', value) + changes.add_option('main', 'Keys', 'default', value) if value: self.var_changed_builtin_keys() else: @@ -675,27 +675,27 @@ def var_changed_are_keys_builtin(self, *params): def var_changed_win_width(self, *params): "Store change to window width." value = self.win_width.get() - changes.additem('main', 'EditorWindow', 'width', value) + changes.add_option('main', 'EditorWindow', 'width', value) def var_changed_win_height(self, *params): "Store change to window height." value = self.win_height.get() - changes.additem('main', 'EditorWindow', 'height', value) + changes.add_option('main', 'EditorWindow', 'height', value) def var_changed_startup_edit(self, *params): "Store change to toggle for starting IDLE in the editor or shell." value = self.startup_edit.get() - changes.additem('main', 'General', 'editor-on-startup', value) + changes.add_option('main', 'General', 'editor-on-startup', value) def var_changed_autosave(self, *params): "Store change to autosave." value = self.autosave.get() - changes.additem('main', 'General', 'autosave', value) + changes.add_option('main', 'General', 'autosave', value) def var_changed_encoding(self, *params): "Store change to encoding." value = self.encoding.get() - changes.additem('main', 'EditorWindow', 'encoding', value) + changes.add_option('main', 'EditorWindow', 'encoding', value) def GetDefaultItems(self): "Return dictionary of default configuration settings." @@ -955,7 +955,7 @@ def on_new_colour_set(self): self.text_highlight_sample.tag_config(sample_element, **{plane:new_colour}) theme = self.custom_theme.get() theme_element = sample_element + '-' + plane - changes.additem('highlight', theme, theme_element, new_colour) + changes.add_option('highlight', theme, theme_element, new_colour) def get_new_theme_name(self, message): "Return name of new theme from query popup." @@ -1129,7 +1129,7 @@ def update_user_help_changed_items(self): "Clear and rebuild the HelpFiles section in changes" changes['main']['HelpFiles'] = {} for num in range(1, len(self.user_helplist) + 1): - changes.additem( + changes.add_option( 'main', 'HelpFiles', str(num), ';'.join(self.user_helplist[num-1][:2])) diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index eca6c2a8d723db..b07595a9aad4ab 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -143,9 +143,9 @@ class ChangesTest(unittest.TestCase): def load(self): changes = self.changes - changes.additem('main', 'Msec', 'mitem', 'mval') - changes.additem('highlight', 'Hsec', 'hitem', 'hval') - changes.additem('keys', 'Ksec', 'kitem', 'kval') + changes.add_option('main', 'Msec', 'mitem', 'mval') + changes.add_option('highlight', 'Hsec', 'hitem', 'hval') + changes.add_option('keys', 'Ksec', 'kitem', 'kval') return changes loaded = {'main': {'Msec': {'mitem': 'mval'}}, @@ -159,21 +159,21 @@ def setUp(self): def test_init(self): self.assertEqual(self.changes, self.empty) - def test_additem(self): + def test_add_option(self): changes = self.load() self.assertEqual(changes, self.loaded) - changes.additem('main', 'Msec', 'mitem', 'mval') + changes.add_option('main', 'Msec', 'mitem', 'mval') self.assertEqual(changes, self.loaded) - def test_set_value(self): # Static function does not touch changes. - setval = self.changes.set_value - self.assertTrue(setval('main', 'Indent', 'what', '0')) - self.assertFalse(setval('main', 'Indent', 'what', '0')) + def test_save_option(self): # Static function does not touch changes. + save_option = self.changes.save_option + self.assertTrue(save_option('main', 'Indent', 'what', '0')) + self.assertFalse(save_option('main', 'Indent', 'what', '0')) self.assertEqual(usermain['Indent']['what'], '0') - self.assertTrue(setval('main', 'Indent', 'use-spaces', '0')) + self.assertTrue(save_option('main', 'Indent', 'use-spaces', '0')) self.assertEqual(usermain['Indent']['use-spaces'], '0') - self.assertTrue(setval('main', 'Indent', 'use-spaces', '1')) + self.assertTrue(save_option('main', 'Indent', 'use-spaces', '1')) self.assertFalse(usermain.has_option('Indent', 'use-spaces')) usermain.remove_section('Indent') @@ -189,16 +189,16 @@ def test_save_added(self): def test_save_help(self): changes = self.changes - changes.set_value('main', 'HelpFiles', 'IDLE', 'idledoc') - changes.additem('main', 'HelpFiles', 'ELDI', 'codeldi') + changes.save_option('main', 'HelpFiles', 'IDLE', 'idledoc') + changes.add_option('main', 'HelpFiles', 'ELDI', 'codeldi') changes.save_all() self.assertFalse(usermain.has_option('HelpFiles', 'IDLE')) self.assertTrue(usermain.has_option('HelpFiles', 'ELDI')) def test_save_default(self): # Cover 2nd and 3rd false branches. changes = self.changes - changes.additem('main', 'Indent', 'use-spaces', '1') - # set_value returns False; cfg_type_changed remains False. + changes.add_option('main', 'Indent', 'use-spaces', '1') + # save_option returns False; cfg_type_changed remains False. # TODO: test that save_all calls usercfg Saves. From 8c708ccef1b7890e2b1e8bac1669e614b153e948 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 7 Jul 2017 02:50:36 -0400 Subject: [PATCH 11/13] Improve save_all docstring as per Louie's comment. --- Lib/idlelib/config.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 974111ca65549b..394a8fcd722d8e 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -826,8 +826,10 @@ def save_option(config_type, section, item, value): return idleConf.userCfg[config_type].SetOption(section, item, value) def save_all(self): - "Save configuration changes to the user config file." + """Save configuration changes to the user config file. + When clear self in preparation for additional changes. + """ idleConf.userCfg['main'].Save() for config_type in self: cfg_type_changed = False From 67d5ea143b146570840bc14c26cdedfd15ea02b6 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 7 Jul 2017 15:04:09 -0400 Subject: [PATCH 12/13] Config changes responding to Cheryl's comments. --- Lib/idlelib/config.py | 6 +++--- Lib/idlelib/idle_test/test_config.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 394a8fcd722d8e..621e0bfc8b4073 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -160,7 +160,7 @@ class IdleConf: (user home dir)/.idlerc/config-{config-type}.cfg """ def __init__(self): - self.config_types = ('main', 'extensions', 'highlight', 'keys') + self.config_types = ('main', 'highlight', 'keys', 'extensions') self.defaultCfg = {} self.userCfg = {} self.cfg = {} # TODO use to select userCfg vs defaultCfg @@ -800,7 +800,7 @@ class ConfigChanges(dict): def __init__(self): "Create a page for each configuration file" self.pages = [] # List of unhashable dicts. - for config_type in ('main', 'highlight', 'keys', 'extensions'): + for config_type in idleConf.config_types: self[config_type] = {} self.pages.append(self[config_type]) @@ -828,7 +828,7 @@ def save_option(config_type, section, item, value): def save_all(self): """Save configuration changes to the user config file. - When clear self in preparation for additional changes. + Then clear self in preparation for additional changes. """ idleConf.userCfg['main'].Save() for config_type in self: diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index b07595a9aad4ab..e157bbb5b52c46 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -141,7 +141,7 @@ class ChangesTest(unittest.TestCase): empty = {'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}} - def load(self): + def load(self): # Test_add_option verifies that this works. changes = self.changes changes.add_option('main', 'Msec', 'mitem', 'mval') changes.add_option('highlight', 'Hsec', 'hitem', 'hval') From 393f5ec1cb98d88a64ecb9f1e679c6701e44bb83 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 7 Jul 2017 15:05:56 -0400 Subject: [PATCH 13/13] Remove unused configdialog.GetDefaultItems. --- Lib/idlelib/configdialog.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index ef3570cd194502..ade6710001d69a 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -697,19 +697,6 @@ def var_changed_encoding(self, *params): value = self.encoding.get() changes.add_option('main', 'EditorWindow', 'encoding', value) - def GetDefaultItems(self): - "Return dictionary of default configuration settings." - d_items={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}} - for config_type in d_items: - sections = idleConf.GetSectionList('default', config_type) - for section in sections: - d_items[config_type][section] = {} - options = idleConf.defaultCfg[config_type].GetOptionList(section) - for option in options: - d_items[config_type][section][option] = ( - idleConf.defaultCfg[config_type].Get(section, option)) - return d_items - def set_theme_type(self): "Set available screen options based on builtin or custom theme." if self.is_builtin_theme.get():