bpo-30779: IDLE -- Factor ConfigChanges class from configdialog, put in config; test. (#2612)

* In config, put dump test code in a function; run it and unittest in 'if __name__ == '__main__'.
* Add class config.ConfigChanges based on changes_class_v4.py on bpo issue.
* Add class test_config.ChangesTest, partly based on configdialog_tests_v1.py on bpo issue.
* Revise configdialog to use ConfigChanges, mostly as specified in tracker msg297804.
* Revise test_configdialog to match configdialog changes.  All tests pass in both files.
* Remove configdialog functions unused or moved to ConfigChanges.
Cheryl Sabella contributed parts of the patch.
This commit is contained in:
terryjreedy 2017-07-07 16:00:57 -04:00 committed by GitHub
parent 1881befb90
commit 349abd9e37
4 changed files with 277 additions and 171 deletions

View file

@ -20,7 +20,7 @@ import tkinter.colorchooser as tkColorChooser
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.query import SectionName, HelpSource
from idlelib.tabbedpages import TabbedPageSet
from idlelib.textview import view_text
changes = ConfigChanges()
class ConfigDialog(Toplevel):
"""Config dialog for IDLE.
@ -71,7 +73,6 @@ class ConfigDialog(Toplevel):
'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 @@ class ConfigDialog(Toplevel):
overriding the default font, we need to write out everything.
"""
value = self.font_name.get()
self.add_changed_item('main', 'EditorWindow', 'font', value)
changes.add_option('main', 'EditorWindow', 'font', value)
value = self.font_size.get()
self.add_changed_item('main', 'EditorWindow', 'font-size', value)
changes.add_option('main', 'EditorWindow', 'font-size', value)
value = self.font_bold.get()
self.add_changed_item('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()
self.add_changed_item('main', 'Indent', 'num-spaces', value)
changes.add_option('main', 'Indent', 'num-spaces', value)
def var_changed_colour(self, *params):
"Process change to color choice."
@ -584,13 +585,13 @@ class ConfigDialog(Toplevel):
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.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:
self.add_changed_item('main', 'Theme', 'name', value)
self.add_changed_item('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()
@ -602,7 +603,7 @@ class ConfigDialog(Toplevel):
"""
value = self.custom_theme.get()
if value != '- no custom themes -':
self.add_changed_item('main', 'Theme', 'name', value)
changes.add_option('main', 'Theme', 'name', value)
self.paint_theme_sample()
def var_changed_is_builtin_theme(self, *params):
@ -612,7 +613,7 @@ class ConfigDialog(Toplevel):
selected theme type.
"""
value = self.is_builtin_theme.get()
self.add_changed_item('main', 'Theme', 'default', value)
changes.add_option('main', 'Theme', 'default', value)
if value:
self.var_changed_builtin_theme()
else:
@ -628,11 +629,11 @@ class ConfigDialog(Toplevel):
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.add_option('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.add_option('extensions', ext_keybind_section, event, value)
def var_changed_builtin_keys(self, *params):
"Process selection of builtin key set."
@ -645,13 +646,13 @@ class ConfigDialog(Toplevel):
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.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:
self.add_changed_item('main', 'Keys', 'name', value)
self.add_changed_item('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)
@ -659,13 +660,13 @@ class ConfigDialog(Toplevel):
"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.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()
self.add_changed_item('main', 'Keys', 'default', value)
changes.add_option('main', 'Keys', 'default', value)
if value:
self.var_changed_builtin_keys()
else:
@ -674,59 +675,27 @@ class ConfigDialog(Toplevel):
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.add_option('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.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()
self.add_changed_item('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()
self.add_changed_item('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()
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
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
changes.add_option('main', 'EditorWindow', 'encoding', value)
def set_theme_type(self):
"Set available screen options based on builtin or custom theme."
@ -769,8 +738,8 @@ class ConfigDialog(Toplevel):
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 +801,8 @@ class ConfigDialog(Toplevel):
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 +832,10 @@ class ConfigDialog(Toplevel):
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 +855,8 @@ class ConfigDialog(Toplevel):
'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 +871,8 @@ class ConfigDialog(Toplevel):
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 +889,8 @@ class ConfigDialog(Toplevel):
'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 +903,8 @@ class ConfigDialog(Toplevel):
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 +942,7 @@ class ConfigDialog(Toplevel):
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.add_option('highlight', theme, theme_element, new_colour)
def get_new_theme_name(self, message):
"Return name of new theme from query popup."
@ -1010,8 +973,8 @@ class ConfigDialog(Toplevel):
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 +1041,8 @@ class ConfigDialog(Toplevel):
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 +1113,10 @@ class ConfigDialog(Toplevel):
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.add_option(
'main', 'HelpFiles', str(num),
';'.join(self.user_helplist[num-1][:2]))
@ -1317,37 +1280,6 @@ class ConfigDialog(Toplevel):
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 deactivate_current_config(self):
"Remove current key bindings."
# Before a config is saved, some cleanup of current
@ -1378,7 +1310,8 @@ class ConfigDialog(Toplevel):
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):