From 5bbb41ef6882f20add1e539006c06897682c8800 Mon Sep 17 00:00:00 2001 From: Cheryl Sabella Date: Thu, 6 Jul 2017 18:40:31 -0400 Subject: [PATCH] bpo-30779: IDLE: Add ConfigChanges class to config.py --- Lib/idlelib/config.py | 87 ++++++++++++++++++++++++++++ Lib/idlelib/idle_test/test_config.py | 40 +++++++++++++ 2 files changed, 127 insertions(+) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 6d683e2b80ac07..c83f7cfc87961d 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -767,6 +767,93 @@ def SaveUserCfgFiles(self): idleConf = IdleConf() +class ConfigChanges(dict): + """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: Add item/value pair to a section. + 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.update(self) + + 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 self: + cfg_type_changed = False + for section in self[config_type]: + if section == 'HelpFiles': + # This section gets completely replaced. + idleConf.userCfg['main'].remove_section('HelpFiles') + cfg_type_changed = True + for item in self[config_type][section]: + value = self[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 _set_user_value(self, config_type, section, item, value): + """Test if new value is same as the default. + + If value is in default, return true if it is removed + from the custom config file. If value is not in + default, return true if it is added or saved to user + config. + """ + 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 delete_section(self, config_type, section): + """Delete a section from config_type. + + 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(_changed_configs) after saving to idleConf. + XXX Mark window *title* when there are changes; unmark here. + """ + for page in self: + self[page].clear() + _warned = set() def _warn(msg, *key): key = (msg,) + key diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index e678cc6f332670..2df06a21657e68 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -6,6 +6,7 @@ from test.support import captured_stderr import unittest from idlelib import config +from idlelib.idle_test.mock_idle import Func # Tests should not depend on fortuitous user configurations. # They must not affect actual user .cfg files. @@ -136,6 +137,45 @@ def test_user_override_keys(self): userkeys.remove_section('Custom Keys') +class ConfigChangesTest(unittest.TestCase): + + def setUp(self): + self.changes = config.ConfigChanges() + self.changes.add_item('main', 'EditorWindow', 'font', 'Test Font') + self.changes.add_item('main', 'General', 'editor-on-startup', 1) + + def tearDown(self): + del self.changes + + def test_add_item(self): + changes = self.changes + expected = {'EditorWindow': {'font': 'Test Font'}, + 'General': {'editor-on-startup': '1'}} + self.assertEqual(changes['main'], expected) + + def test_save_all(self): + changes = self.changes + idleConf.userCfg['main'].Save = Func() + changes.save_all() + self.assertTrue(idleConf.userCfg['main'].Save.called) + self.assertEqual(testcfg['main']['EditorWindow']['font'], 'Test Font') + self.assertEqual(testcfg['main']['General']['editor-on-startup'], '1') + + def test_clear(self): + changes = self.changes + expected = {'main': {}, 'highlight': {}, 'keys': {}, + 'extensions': {}} + self.assertNotEqual(changes, expected) + changes.clear() + self.assertEqual(changes, expected) + + def test_delete_section(self): + changes = self.changes + changes.delete_section('main', 'EditorWindow') + expected = {'General': {'editor-on-startup': '1'}} + self.assertEqual(changes['main'], expected) + + class WarningTest(unittest.TestCase): def test_warn(self):