cpython: e6e6c71776b0 (original) (raw)
Mercurial > cpython
changeset 102300:e6e6c71776b0
Issue #27173: Add 'IDLE Modern Unix' to the built-in key sets. Make the default key set depend on the platform. Add tests for changes to the config module. [#27173]
Terry Jan Reedy tjreedy@udel.edu | |
---|---|
date | Sun, 10 Jul 2016 13:46:34 -0400 |
parents | 31cdf23da19d |
children | a9c11fecd83b |
files | Lib/idlelib/config-keys.def Lib/idlelib/config-main.def Lib/idlelib/config.py Lib/idlelib/configdialog.py Lib/idlelib/idle_test/test_config.py |
diffstat | 5 files changed, 270 insertions(+), 57 deletions(-)[+] [-] Lib/idlelib/config-keys.def 51 Lib/idlelib/config-main.def 4 Lib/idlelib/config.py 130 Lib/idlelib/configdialog.py 44 Lib/idlelib/idle_test/test_config.py 98 |
line wrap: on
line diff
--- a/Lib/idlelib/config-keys.def +++ b/Lib/idlelib/config-keys.def @@ -109,6 +109,57 @@ change-indentwidth= del-word-left= del-word-right= +[IDLE Modern Unix] +copy = +cut = +paste = +beginning-of-line = +center-insert = +close-all-windows = +close-window = +do-nothing = +end-of-file = +history-next = +history-previous = +interrupt-execution = +view-restart = +restart-shell = +open-class-browser = +open-module = +open-new-window = +open-window-from-file = +plain-newline-and-indent = +print-window = +python-context-help = +python-docs = +redo = +remove-selection = +save-copy-of-window-as-file = +save-window-as-file = +save-window = +select-all = +toggle-auto-coloring = +undo = +find = +find-again = +find-in-files = +find-selection = +replace = +goto-line = +smart-backspace = +newline-and-indent = +smart-indent = +indent-region = +dedent-region = +comment-region = +uncomment-region = +tabify-region = +untabify-region = +toggle-tabs = +change-indentwidth = +del-word-left = +del-word-right = + [IDLE Classic Mac] copy= cut=
--- a/Lib/idlelib/config-main.def +++ b/Lib/idlelib/config-main.def @@ -70,7 +70,9 @@ name2= [Keys] default= 1 -name= IDLE Classic Windows +name= +name2= +# name2 set in user config-main.cfg for keys added after 2016 July 1 [History] cyclic=1
--- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -234,10 +234,7 @@ class IdleConf: ' from section %r: %r' % (type, option, section, self.userCfg[configType].Get(section, option, raw=raw)))
try:[](#l3.7)
print(warning, file=sys.stderr)[](#l3.8)
except OSError:[](#l3.9)
pass[](#l3.10)
_warn(warning, configType, section, option)[](#l3.11) try:[](#l3.12) if self.defaultCfg[configType].has_option(section,option):[](#l3.13) return self.defaultCfg[configType].Get([](#l3.14)
@@ -251,10 +248,7 @@ class IdleConf: ' from section %r.\n' ' returning default value: %r' % (option, section, default))
try:[](#l3.19)
print(warning, file=sys.stderr)[](#l3.20)
except OSError:[](#l3.21)
pass[](#l3.22)
_warn(warning, configType, section, option)[](#l3.23) return default[](#l3.24)
def SetOption(self, configType, section, option, value): @@ -362,47 +356,68 @@ class IdleConf: '\n from theme %r.\n' ' returning default color: %r' % (element, themeName, theme[element]))
try:[](#l3.31)
print(warning, file=sys.stderr)[](#l3.32)
except OSError:[](#l3.33)
pass[](#l3.34)
_warn(warning, 'highlight', themeName, element)[](#l3.35) theme[element] = cfgParser.Get([](#l3.36) themeName, element, default=theme[element])[](#l3.37) return theme[](#l3.38)
"""Return the name of the currently active text color theme.[](#l3.41)
"Return the name of the currently active text color theme."[](#l3.42)
return self.current_colors_and_keys('Theme')[](#l3.43)
idlelib.config-main.def includes this section[](#l3.45)
- def CurrentKeys(self):
"""Return the name of the currently active key set."""[](#l3.47)
return self.current_colors_and_keys('Keys')[](#l3.48)
- def current_colors_and_keys(self, section):
"""Return the currently active name for Theme or Keys section.[](#l3.51)
idlelib.config-main.def ('default') includes these sections[](#l3.53)
+ [Theme] default= 1 name= IDLE Classic name2=
# name2 set in user config-main.cfg for themes added after 2015 Oct 1[](#l3.59)
[Keys][](#l3.61)
default= 1[](#l3.62)
name=[](#l3.63)
name2=[](#l3.64)
Item name2 is needed because setting name to a new builtin[](#l3.66)
causes older IDLEs to display multiple error messages or quit.[](#l3.67)
Item 'name2', is used for built-in ('default') themes and keys[](#l3.68)
added after 2015 Oct 1 and 2016 July 1. This kludge is needed[](#l3.69)
because setting 'name' to a builtin not defined in older IDLEs[](#l3.70)
to display multiple error messages or quit.[](#l3.71) See https://bugs.python.org/issue25313.[](#l3.72)
When default = True, name2 takes precedence over name,[](#l3.73)
while older IDLEs will just use name.[](#l3.74)
When default = True, 'name2' takes precedence over 'name',[](#l3.75)
while older IDLEs will just use name. When default = False,[](#l3.76)
'name2' may still be set, but it is ignored.[](#l3.77) """[](#l3.78)
cfgname = 'highlight' if section == 'Theme' else 'keys'[](#l3.79) default = self.GetOption('main', 'Theme', 'default',[](#l3.80) type='bool', default=True)[](#l3.81)
name = ''[](#l3.82) if default:[](#l3.83)
theme = self.GetOption('main', 'Theme', 'name2', default='')[](#l3.84)
if default and not theme or not default:[](#l3.85)
theme = self.GetOption('main', 'Theme', 'name', default='')[](#l3.86)
source = self.defaultCfg if default else self.userCfg[](#l3.87)
if source['highlight'].has_section(theme):[](#l3.88)
return theme[](#l3.89)
name = self.GetOption('main', section, 'name2', default='')[](#l3.90)
if not name:[](#l3.91)
name = self.GetOption('main', section, 'name', default='')[](#l3.92)
if name:[](#l3.93)
source = self.defaultCfg if default else self.userCfg[](#l3.94)
if source[cfgname].has_section(name):[](#l3.95)
return name[](#l3.96)
return "IDLE Classic" if section == 'Theme' else self.default_keys()[](#l3.97)
- @staticmethod
- def default_keys():
if sys.platform[:3] == 'win':[](#l3.101)
return 'IDLE Classic Windows'[](#l3.102)
elif sys.platform == 'darwin':[](#l3.103)
return 'IDLE Classic OSX'[](#l3.104) else:[](#l3.105)
return "IDLE Classic"[](#l3.106)
return 'IDLE Modern Unix'[](#l3.107)
- def CurrentKeys(self):
"Return the name of the currently active key set."[](#l3.110)
return self.GetOption('main', 'Keys', 'name', default='')[](#l3.111)
- def GetExtensions(self, active_only=True,
editor_only=False, shell_only=False):[](#l3.115) """Return extensions in default and user config-extensions files.[](#l3.116)
If active_only True, only return active (enabled) extensions @@ -422,7 +437,7 @@ class IdleConf: if self.GetOption('extensions', extn, 'enable', default=True, type='bool'): #the extension is enabled
if editor_only or shell_only: # TODO if both, contradictory[](#l3.123)
if editor_only or shell_only: # TODO both True contradict[](#l3.124) if editor_only:[](#l3.125) option = "enable_editor"[](#l3.126) else:[](#l3.127)
@@ -527,7 +542,8 @@ class IdleConf: eventStr - virtual event, including brackets, as in '<>'. """ eventName = eventStr[2:-2] #trim off the angle brackets
binding = self.GetOption('keys', keySetName, eventName, default='').split()[](#l3.132)
binding = self.GetOption('keys', keySetName, eventName, default='',[](#l3.133)
warn_on_default=False).split()[](#l3.134) return binding[](#l3.135)
def GetCurrentKeySet(self): @@ -638,20 +654,28 @@ class IdleConf: '<>': [''] } if keySetName:
for event in keyBindings:[](#l3.142)
binding = self.GetKeyBinding(keySetName, event)[](#l3.143)
if binding:[](#l3.144)
keyBindings[event] = binding[](#l3.145)
else: #we are going to return a default, print warning[](#l3.146)
warning=('\n Warning: config.py - IdleConf.GetCoreKeys'[](#l3.147)
' -\n problem retrieving key binding for event %r'[](#l3.148)
'\n from key set %r.\n'[](#l3.149)
' returning default value: %r' %[](#l3.150)
(event, keySetName, keyBindings[event]))[](#l3.151)
try:[](#l3.152)
print(warning, file=sys.stderr)[](#l3.153)
except OSError:[](#l3.154)
pass[](#l3.155)
if not (self.userCfg['keys'].has_section(keySetName) or[](#l3.156)
self.defaultCfg['keys'].has_section(keySetName)):[](#l3.157)
warning = ([](#l3.158)
'\n Warning: config.py - IdleConf.GetCoreKeys -\n'[](#l3.159)
' key set %r is not defined, using default bindings.' %[](#l3.160)
(keySetName,)[](#l3.161)
)[](#l3.162)
_warn(warning, 'keys', keySetName)[](#l3.163)
else:[](#l3.164)
for event in keyBindings:[](#l3.165)
binding = self.GetKeyBinding(keySetName, event)[](#l3.166)
if binding:[](#l3.167)
keyBindings[event] = binding[](#l3.168)
else: #we are going to return a default, print warning[](#l3.169)
warning = ([](#l3.170)
'\n Warning: config.py - IdleConf.GetCoreKeys -\n'[](#l3.171)
' problem retrieving key binding for event %r\n'[](#l3.172)
' from key set %r.\n'[](#l3.173)
' returning default value: %r' %[](#l3.174)
(event, keySetName, keyBindings[event])[](#l3.175)
)[](#l3.176)
_warn(warning, 'keys', keySetName, event)[](#l3.177) return keyBindings[](#l3.178)
def GetExtraHelpSourceList(self, configSet): @@ -735,6 +759,18 @@ class IdleConf: idleConf = IdleConf() + +_warned = set() +def _warn(msg, *key):
- key = (msg,) + key
- if key not in _warned:
try:[](#l3.190)
print(msg, file=sys.stderr)[](#l3.191)
except OSError:[](#l3.192)
pass[](#l3.193)
_warned.add(key)[](#l3.194)
TODO Revise test output, write expanded unittest
--- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -341,6 +341,7 @@ class ConfigDialog(Toplevel): buttonSaveCustomKeys = Button( frames[1], text='Save as New Custom Key Set', command=self.SaveAsNewKeySet)
self.new_custom_keys = Label(frames[0], bd=2)[](#l4.7)
##widget packing #body @@ -361,6 +362,7 @@ class ConfigDialog(Toplevel): self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS) self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW) self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW)
self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)[](#l4.15) self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)[](#l4.16) buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)[](#l4.17) frames[0].pack(side=TOP, fill=BOTH, expand=True)[](#l4.18)
@@ -514,10 +516,11 @@ class ConfigDialog(Toplevel): self.OnNewColourSet() def VarChanged_builtinTheme(self, *params):
oldthemes = ('IDLE Classic', 'IDLE New')[](#l4.23) value = self.builtinTheme.get()[](#l4.24)
if value == 'IDLE Dark':[](#l4.25)
if idleConf.GetOption('main', 'Theme', 'name') != 'IDLE New':[](#l4.26)
self.AddChangedItem('main', 'Theme', 'name', 'IDLE Classic')[](#l4.27)
if value not in oldthemes:[](#l4.28)
if idleConf.GetOption('main', 'Theme', 'name') not in oldthemes:[](#l4.29)
self.AddChangedItem('main', 'Theme', 'name', oldthemes[0])[](#l4.30) self.AddChangedItem('main', 'Theme', 'name2', value)[](#l4.31) self.new_custom_theme.config(text='New theme, see Help',[](#l4.32) fg='#500000')[](#l4.33)
@@ -557,8 +560,23 @@ class ConfigDialog(Toplevel): self.AddChangedItem('extensions', extKeybindSection, event, value) def VarChanged_builtinKeys(self, *params):
oldkeys = ([](#l4.38)
'IDLE Classic Windows',[](#l4.39)
'IDLE Classic Unix',[](#l4.40)
'IDLE Classic Mac',[](#l4.41)
'IDLE Classic OSX',[](#l4.42)
)[](#l4.43) value = self.builtinKeys.get()[](#l4.44)
self.AddChangedItem('main', 'Keys', 'name', value)[](#l4.45)
if value not in oldkeys:[](#l4.46)
if idleConf.GetOption('main', 'Keys', 'name') not in oldkeys:[](#l4.47)
self.AddChangedItem('main', 'Keys', 'name', oldkeys[0])[](#l4.48)
self.AddChangedItem('main', 'Keys', 'name2', value)[](#l4.49)
self.new_custom_keys.config(text='New key set, see Help',[](#l4.50)
fg='#500000')[](#l4.51)
else:[](#l4.52)
self.AddChangedItem('main', 'Keys', 'name', value)[](#l4.53)
self.AddChangedItem('main', 'Keys', 'name2', '')[](#l4.54)
self.new_custom_keys.config(text='', fg='black')[](#l4.55) self.LoadKeysList(value)[](#l4.56)
def VarChanged_customKeys(self, *params): @@ -767,8 +785,10 @@ class ConfigDialog(Toplevel): else: self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) #revert to default key set
self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default'))[](#l4.63)
self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name'))[](#l4.64)
self.keysAreBuiltin.set(idleConf.defaultCfg['main'][](#l4.65)
.Get('Keys', 'default'))[](#l4.66)
self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')[](#l4.67)
or idleConf.default_keys())[](#l4.68) #user can't back out of these changes, they must be applied now[](#l4.69) self.SaveAllChangedConfigs()[](#l4.70) self.ActivateConfigChanges()[](#l4.71)
@@ -1067,7 +1087,7 @@ class ConfigDialog(Toplevel): self.optMenuKeysCustom.SetMenu(itemList, currentOption) itemList = idleConf.GetSectionList('default', 'keys') itemList.sort()
self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0])[](#l4.76)
self.optMenuKeysBuiltin.SetMenu(itemList, idleConf.default_keys())[](#l4.77) self.SetKeysType()[](#l4.78) ##load keyset element list[](#l4.79) keySetName = idleConf.CurrentKeys()[](#l4.80)
@@ -1369,12 +1389,18 @@ machine. Some do not take affect until I [Cancel] only cancels changes made since the last save. ''' help_pages = {
Highlighting: The IDLE Dark color theme is new in October 2015. It can only be used with older IDLE releases if it is saved as a custom theme, with a different name. -''' +''',
+Keys: +The IDLE Modern Unix key set is new in June 2016. It can only +be used with older IDLE releases if it is saved as a custom +key set, with a different name. +''', }
new file mode 100644 --- /dev/null +++ b/Lib/idlelib/idle_test/test_config.py @@ -0,0 +1,98 @@ +'''Test idlelib.config. + +Much is tested by opening config dialog live or in test_configdialog. +Coverage: 27% +''' +from sys import modules +from test.support import captured_stderr +from tkinter import Tk +import unittest +from idlelib import config + +# 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. + +idleConf = config.idleConf +usercfg = idleConf.userCfg +testcfg = {} +usermain = testcfg['main'] = config.IdleUserConfParser('') # filename +userhigh = testcfg['highlight'] = config.IdleUserConfParser('') +userkeys = testcfg['keys'] = config.IdleUserConfParser('') + +def setUpModule():
+ + +class CurrentColorKeysTest(unittest.TestCase):
The 5 correct patterns are possible results of config dialog.[](#l5.37)
- """
- colorkeys = idleConf.current_colors_and_keys
- def test_old_default(self):
# name2 must be blank[](#l5.42)
usermain.read_string('''[](#l5.43)
[Theme][](#l5.44)
default= 1[](#l5.45)
''')[](#l5.46)
self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic')[](#l5.47)
usermain['Theme']['name'] = 'IDLE New'[](#l5.48)
self.assertEqual(self.colorkeys('Theme'), 'IDLE New')[](#l5.49)
usermain['Theme']['name'] = 'non-default' # error[](#l5.50)
self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic')[](#l5.51)
usermain.remove_section('Theme')[](#l5.52)
- def test_new_default(self):
# name2 overrides name[](#l5.55)
usermain.read_string('''[](#l5.56)
[Theme][](#l5.57)
default= 1[](#l5.58)
name= IDLE New[](#l5.59)
name2= IDLE Dark[](#l5.60)
''')[](#l5.61)
self.assertEqual(self.colorkeys('Theme'), 'IDLE Dark')[](#l5.62)
usermain['Theme']['name2'] = 'non-default' # error[](#l5.63)
self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic')[](#l5.64)
usermain.remove_section('Theme')[](#l5.65)
- def test_user_override(self):
# name2 does not matter[](#l5.68)
usermain.read_string('''[](#l5.69)
[Theme][](#l5.70)
default= 0[](#l5.71)
name= Custom Dark[](#l5.72)
''') # error until set userhigh[](#l5.73)
self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic')[](#l5.74)
userhigh.read_string('[Custom Dark]\na=b')[](#l5.75)
self.assertEqual(self.colorkeys('Theme'), 'Custom Dark')[](#l5.76)
usermain['Theme']['name2'] = 'IDLE Dark'[](#l5.77)
self.assertEqual(self.colorkeys('Theme'), 'Custom Dark')[](#l5.78)
usermain.remove_section('Theme')[](#l5.79)
userhigh.remove_section('Custom Dark')[](#l5.80)
+ + +class WarningTest(unittest.TestCase): +
- def test_warn(self):
Equal = self.assertEqual[](#l5.86)
config._warned = set()[](#l5.87)
with captured_stderr() as stderr:[](#l5.88)
config._warn('warning', 'key')[](#l5.89)
Equal(config._warned, {('warning','key')})[](#l5.90)
Equal(stderr.getvalue(), 'warning'+'\n')[](#l5.91)
with captured_stderr() as stderr:[](#l5.92)
config._warn('warning', 'key')[](#l5.93)
Equal(stderr.getvalue(), '')[](#l5.94)
with captured_stderr() as stderr:[](#l5.95)
config._warn('warn2', 'yek')[](#l5.96)
Equal(config._warned, {('warning','key'), ('warn2','yek')})[](#l5.97)
Equal(stderr.getvalue(), 'warn2'+'\n')[](#l5.98)