Issue #7632: Fix a serious wrong output bug for string -> float conversion.
[python.git] / Lib / idlelib / configHandler.py
blob294787399b9d82eb4706f286b8318c8ad86e33bb
1 """Provides access to stored IDLE configuration information.
3 Refer to the comments at the beginning of config-main.def for a description of
4 the available configuration files and the design implemented to update user
5 configuration information. In particular, user configuration choices which
6 duplicate the defaults will be removed from the user's configuration files,
7 and if a file becomes empty, it will be deleted.
9 The contents of the user files may be altered using the Options/Configure IDLE
10 menu to access the configuration GUI (configDialog.py), or manually.
12 Throughout this module there is an emphasis on returning useable defaults
13 when a problem occurs in returning a requested configuration value back to
14 idle. This is to allow IDLE to continue to function in spite of errors in
15 the retrieval of config information. When a default is returned instead of
16 a requested config value, a message is printed to stderr to aid in
17 configuration problem notification and resolution.
19 """
20 import os
21 import sys
22 import string
23 import macosxSupport
24 from ConfigParser import ConfigParser, NoOptionError, NoSectionError
26 class InvalidConfigType(Exception): pass
27 class InvalidConfigSet(Exception): pass
28 class InvalidFgBg(Exception): pass
29 class InvalidTheme(Exception): pass
31 class IdleConfParser(ConfigParser):
32 """
33 A ConfigParser specialised for idle configuration file handling
34 """
35 def __init__(self, cfgFile, cfgDefaults=None):
36 """
37 cfgFile - string, fully specified configuration file name
38 """
39 self.file=cfgFile
40 ConfigParser.__init__(self,defaults=cfgDefaults)
42 def Get(self, section, option, type=None, default=None, raw=False):
43 """
44 Get an option value for given section/option or return default.
45 If type is specified, return as type.
46 """
47 if not self.has_option(section, option):
48 return default
49 if type=='bool':
50 return self.getboolean(section, option)
51 elif type=='int':
52 return self.getint(section, option)
53 else:
54 return self.get(section, option, raw=raw)
56 def GetOptionList(self,section):
57 """
58 Get an option list for given section
59 """
60 if self.has_section(section):
61 return self.options(section)
62 else: #return a default value
63 return []
65 def Load(self):
66 """
67 Load the configuration file from disk
68 """
69 self.read(self.file)
71 class IdleUserConfParser(IdleConfParser):
72 """
73 IdleConfigParser specialised for user configuration handling.
74 """
76 def AddSection(self,section):
77 """
78 if section doesn't exist, add it
79 """
80 if not self.has_section(section):
81 self.add_section(section)
83 def RemoveEmptySections(self):
84 """
85 remove any sections that have no options
86 """
87 for section in self.sections():
88 if not self.GetOptionList(section):
89 self.remove_section(section)
91 def IsEmpty(self):
92 """
93 Remove empty sections and then return 1 if parser has no sections
94 left, else return 0.
95 """
96 self.RemoveEmptySections()
97 if self.sections():
98 return 0
99 else:
100 return 1
102 def RemoveOption(self,section,option):
104 If section/option exists, remove it.
105 Returns 1 if option was removed, 0 otherwise.
107 if self.has_section(section):
108 return self.remove_option(section,option)
110 def SetOption(self,section,option,value):
112 Sets option to value, adding section if required.
113 Returns 1 if option was added or changed, otherwise 0.
115 if self.has_option(section,option):
116 if self.get(section,option)==value:
117 return 0
118 else:
119 self.set(section,option,value)
120 return 1
121 else:
122 if not self.has_section(section):
123 self.add_section(section)
124 self.set(section,option,value)
125 return 1
127 def RemoveFile(self):
129 Removes the user config file from disk if it exists.
131 if os.path.exists(self.file):
132 os.remove(self.file)
134 def Save(self):
135 """Update user configuration file.
137 Remove empty sections. If resulting config isn't empty, write the file
138 to disk. If config is empty, remove the file from disk if it exists.
141 if not self.IsEmpty():
142 fname = self.file
143 try:
144 cfgFile = open(fname, 'w')
145 except IOError:
146 os.unlink(fname)
147 cfgFile = open(fname, 'w')
148 self.write(cfgFile)
149 else:
150 self.RemoveFile()
152 class IdleConf:
154 holds config parsers for all idle config files:
155 default config files
156 (idle install dir)/config-main.def
157 (idle install dir)/config-extensions.def
158 (idle install dir)/config-highlight.def
159 (idle install dir)/config-keys.def
160 user config files
161 (user home dir)/.idlerc/config-main.cfg
162 (user home dir)/.idlerc/config-extensions.cfg
163 (user home dir)/.idlerc/config-highlight.cfg
164 (user home dir)/.idlerc/config-keys.cfg
166 def __init__(self):
167 self.defaultCfg={}
168 self.userCfg={}
169 self.cfg={}
170 self.CreateConfigHandlers()
171 self.LoadCfgFiles()
172 #self.LoadCfg()
174 def CreateConfigHandlers(self):
176 set up a dictionary of config parsers for default and user
177 configurations respectively
179 #build idle install path
180 if __name__ != '__main__': # we were imported
181 idleDir=os.path.dirname(__file__)
182 else: # we were exec'ed (for testing only)
183 idleDir=os.path.abspath(sys.path[0])
184 userDir=self.GetUserCfgDir()
185 configTypes=('main','extensions','highlight','keys')
186 defCfgFiles={}
187 usrCfgFiles={}
188 for cfgType in configTypes: #build config file names
189 defCfgFiles[cfgType]=os.path.join(idleDir,'config-'+cfgType+'.def')
190 usrCfgFiles[cfgType]=os.path.join(userDir,'config-'+cfgType+'.cfg')
191 for cfgType in configTypes: #create config parsers
192 self.defaultCfg[cfgType]=IdleConfParser(defCfgFiles[cfgType])
193 self.userCfg[cfgType]=IdleUserConfParser(usrCfgFiles[cfgType])
195 def GetUserCfgDir(self):
197 Creates (if required) and returns a filesystem directory for storing
198 user config files.
201 cfgDir = '.idlerc'
202 userDir = os.path.expanduser('~')
203 if userDir != '~': # expanduser() found user home dir
204 if not os.path.exists(userDir):
205 warn = ('\n Warning: os.path.expanduser("~") points to\n '+
206 userDir+',\n but the path does not exist.\n')
207 try:
208 sys.stderr.write(warn)
209 except IOError:
210 pass
211 userDir = '~'
212 if userDir == "~": # still no path to home!
213 # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
214 userDir = os.getcwd()
215 userDir = os.path.join(userDir, cfgDir)
216 if not os.path.exists(userDir):
217 try:
218 os.mkdir(userDir)
219 except (OSError, IOError):
220 warn = ('\n Warning: unable to create user config directory\n'+
221 userDir+'\n Check path and permissions.\n Exiting!\n\n')
222 sys.stderr.write(warn)
223 raise SystemExit
224 return userDir
226 def GetOption(self, configType, section, option, default=None, type=None,
227 warn_on_default=True, raw=False):
229 Get an option value for given config type and given general
230 configuration section/option or return a default. If type is specified,
231 return as type. Firstly the user configuration is checked, with a
232 fallback to the default configuration, and a final 'catch all'
233 fallback to a useable passed-in default if the option isn't present in
234 either the user or the default configuration.
235 configType must be one of ('main','extensions','highlight','keys')
236 If a default is returned, and warn_on_default is True, a warning is
237 printed to stderr.
240 if self.userCfg[configType].has_option(section,option):
241 return self.userCfg[configType].Get(section, option,
242 type=type, raw=raw)
243 elif self.defaultCfg[configType].has_option(section,option):
244 return self.defaultCfg[configType].Get(section, option,
245 type=type, raw=raw)
246 else: #returning default, print warning
247 if warn_on_default:
248 warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
249 ' problem retrieving configuration option %r\n'
250 ' from section %r.\n'
251 ' returning default value: %r\n' %
252 (option, section, default))
253 try:
254 sys.stderr.write(warning)
255 except IOError:
256 pass
257 return default
259 def SetOption(self, configType, section, option, value):
260 """In user's config file, set section's option to value.
263 self.userCfg[configType].SetOption(section, option, value)
265 def GetSectionList(self, configSet, configType):
267 Get a list of sections from either the user or default config for
268 the given config type.
269 configSet must be either 'user' or 'default'
270 configType must be one of ('main','extensions','highlight','keys')
272 if not (configType in ('main','extensions','highlight','keys')):
273 raise InvalidConfigType, 'Invalid configType specified'
274 if configSet == 'user':
275 cfgParser=self.userCfg[configType]
276 elif configSet == 'default':
277 cfgParser=self.defaultCfg[configType]
278 else:
279 raise InvalidConfigSet, 'Invalid configSet specified'
280 return cfgParser.sections()
282 def GetHighlight(self, theme, element, fgBg=None):
284 return individual highlighting theme elements.
285 fgBg - string ('fg'or'bg') or None, if None return a dictionary
286 containing fg and bg colours (appropriate for passing to Tkinter in,
287 e.g., a tag_config call), otherwise fg or bg colour only as specified.
289 if self.defaultCfg['highlight'].has_section(theme):
290 themeDict=self.GetThemeDict('default',theme)
291 else:
292 themeDict=self.GetThemeDict('user',theme)
293 fore=themeDict[element+'-foreground']
294 if element=='cursor': #there is no config value for cursor bg
295 back=themeDict['normal-background']
296 else:
297 back=themeDict[element+'-background']
298 highlight={"foreground": fore,"background": back}
299 if not fgBg: #return dict of both colours
300 return highlight
301 else: #return specified colour only
302 if fgBg == 'fg':
303 return highlight["foreground"]
304 if fgBg == 'bg':
305 return highlight["background"]
306 else:
307 raise InvalidFgBg, 'Invalid fgBg specified'
309 def GetThemeDict(self,type,themeName):
311 type - string, 'default' or 'user' theme type
312 themeName - string, theme name
313 Returns a dictionary which holds {option:value} for each element
314 in the specified theme. Values are loaded over a set of ultimate last
315 fallback defaults to guarantee that all theme elements are present in
316 a newly created theme.
318 if type == 'user':
319 cfgParser=self.userCfg['highlight']
320 elif type == 'default':
321 cfgParser=self.defaultCfg['highlight']
322 else:
323 raise InvalidTheme, 'Invalid theme type specified'
324 #foreground and background values are provded for each theme element
325 #(apart from cursor) even though all these values are not yet used
326 #by idle, to allow for their use in the future. Default values are
327 #generally black and white.
328 theme={ 'normal-foreground':'#000000',
329 'normal-background':'#ffffff',
330 'keyword-foreground':'#000000',
331 'keyword-background':'#ffffff',
332 'builtin-foreground':'#000000',
333 'builtin-background':'#ffffff',
334 'comment-foreground':'#000000',
335 'comment-background':'#ffffff',
336 'string-foreground':'#000000',
337 'string-background':'#ffffff',
338 'definition-foreground':'#000000',
339 'definition-background':'#ffffff',
340 'hilite-foreground':'#000000',
341 'hilite-background':'gray',
342 'break-foreground':'#ffffff',
343 'break-background':'#000000',
344 'hit-foreground':'#ffffff',
345 'hit-background':'#000000',
346 'error-foreground':'#ffffff',
347 'error-background':'#000000',
348 #cursor (only foreground can be set)
349 'cursor-foreground':'#000000',
350 #shell window
351 'stdout-foreground':'#000000',
352 'stdout-background':'#ffffff',
353 'stderr-foreground':'#000000',
354 'stderr-background':'#ffffff',
355 'console-foreground':'#000000',
356 'console-background':'#ffffff' }
357 for element in theme.keys():
358 if not cfgParser.has_option(themeName,element):
359 #we are going to return a default, print warning
360 warning=('\n Warning: configHandler.py - IdleConf.GetThemeDict'
361 ' -\n problem retrieving theme element %r'
362 '\n from theme %r.\n'
363 ' returning default value: %r\n' %
364 (element, themeName, theme[element]))
365 try:
366 sys.stderr.write(warning)
367 except IOError:
368 pass
369 colour=cfgParser.Get(themeName,element,default=theme[element])
370 theme[element]=colour
371 return theme
373 def CurrentTheme(self):
375 Returns the name of the currently active theme
377 return self.GetOption('main','Theme','name',default='')
379 def CurrentKeys(self):
381 Returns the name of the currently active key set
383 return self.GetOption('main','Keys','name',default='')
385 def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
387 Gets a list of all idle extensions declared in the config files.
388 active_only - boolean, if true only return active (enabled) extensions
390 extns=self.RemoveKeyBindNames(
391 self.GetSectionList('default','extensions'))
392 userExtns=self.RemoveKeyBindNames(
393 self.GetSectionList('user','extensions'))
394 for extn in userExtns:
395 if extn not in extns: #user has added own extension
396 extns.append(extn)
397 if active_only:
398 activeExtns=[]
399 for extn in extns:
400 if self.GetOption('extensions', extn, 'enable', default=True,
401 type='bool'):
402 #the extension is enabled
403 if editor_only or shell_only:
404 if editor_only:
405 option = "enable_editor"
406 else:
407 option = "enable_shell"
408 if self.GetOption('extensions', extn,option,
409 default=True, type='bool',
410 warn_on_default=False):
411 activeExtns.append(extn)
412 else:
413 activeExtns.append(extn)
414 return activeExtns
415 else:
416 return extns
418 def RemoveKeyBindNames(self,extnNameList):
419 #get rid of keybinding section names
420 names=extnNameList
421 kbNameIndicies=[]
422 for name in names:
423 if name.endswith(('_bindings', '_cfgBindings')):
424 kbNameIndicies.append(names.index(name))
425 kbNameIndicies.sort()
426 kbNameIndicies.reverse()
427 for index in kbNameIndicies: #delete each keybinding section name
428 del(names[index])
429 return names
431 def GetExtnNameForEvent(self,virtualEvent):
433 Returns the name of the extension that virtualEvent is bound in, or
434 None if not bound in any extension.
435 virtualEvent - string, name of the virtual event to test for, without
436 the enclosing '<< >>'
438 extName=None
439 vEvent='<<'+virtualEvent+'>>'
440 for extn in self.GetExtensions(active_only=0):
441 for event in self.GetExtensionKeys(extn).keys():
442 if event == vEvent:
443 extName=extn
444 return extName
446 def GetExtensionKeys(self,extensionName):
448 returns a dictionary of the configurable keybindings for a particular
449 extension,as they exist in the dictionary returned by GetCurrentKeySet;
450 that is, where previously used bindings are disabled.
452 keysName=extensionName+'_cfgBindings'
453 activeKeys=self.GetCurrentKeySet()
454 extKeys={}
455 if self.defaultCfg['extensions'].has_section(keysName):
456 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
457 for eventName in eventNames:
458 event='<<'+eventName+'>>'
459 binding=activeKeys[event]
460 extKeys[event]=binding
461 return extKeys
463 def __GetRawExtensionKeys(self,extensionName):
465 returns a dictionary of the configurable keybindings for a particular
466 extension, as defined in the configuration files, or an empty dictionary
467 if no bindings are found
469 keysName=extensionName+'_cfgBindings'
470 extKeys={}
471 if self.defaultCfg['extensions'].has_section(keysName):
472 eventNames=self.defaultCfg['extensions'].GetOptionList(keysName)
473 for eventName in eventNames:
474 binding=self.GetOption('extensions',keysName,
475 eventName,default='').split()
476 event='<<'+eventName+'>>'
477 extKeys[event]=binding
478 return extKeys
480 def GetExtensionBindings(self,extensionName):
482 Returns a dictionary of all the event bindings for a particular
483 extension. The configurable keybindings are returned as they exist in
484 the dictionary returned by GetCurrentKeySet; that is, where re-used
485 keybindings are disabled.
487 bindsName=extensionName+'_bindings'
488 extBinds=self.GetExtensionKeys(extensionName)
489 #add the non-configurable bindings
490 if self.defaultCfg['extensions'].has_section(bindsName):
491 eventNames=self.defaultCfg['extensions'].GetOptionList(bindsName)
492 for eventName in eventNames:
493 binding=self.GetOption('extensions',bindsName,
494 eventName,default='').split()
495 event='<<'+eventName+'>>'
496 extBinds[event]=binding
498 return extBinds
500 def GetKeyBinding(self, keySetName, eventStr):
502 returns the keybinding for a specific event.
503 keySetName - string, name of key binding set
504 eventStr - string, the virtual event we want the binding for,
505 represented as a string, eg. '<<event>>'
507 eventName=eventStr[2:-2] #trim off the angle brackets
508 binding=self.GetOption('keys',keySetName,eventName,default='').split()
509 return binding
511 def GetCurrentKeySet(self):
512 result = self.GetKeySet(self.CurrentKeys())
514 if macosxSupport.runningAsOSXApp():
515 # We're using AquaTk, replace all keybingings that use the
516 # Alt key by ones that use the Option key because the former
517 # don't work reliably.
518 for k, v in result.items():
519 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
520 if v != v2:
521 result[k] = v2
523 return result
525 def GetKeySet(self,keySetName):
527 Returns a dictionary of: all requested core keybindings, plus the
528 keybindings for all currently active extensions. If a binding defined
529 in an extension is already in use, that binding is disabled.
531 keySet=self.GetCoreKeys(keySetName)
532 activeExtns=self.GetExtensions(active_only=1)
533 for extn in activeExtns:
534 extKeys=self.__GetRawExtensionKeys(extn)
535 if extKeys: #the extension defines keybindings
536 for event in extKeys.keys():
537 if extKeys[event] in keySet.values():
538 #the binding is already in use
539 extKeys[event]='' #disable this binding
540 keySet[event]=extKeys[event] #add binding
541 return keySet
543 def IsCoreBinding(self,virtualEvent):
545 returns true if the virtual event is bound in the core idle keybindings.
546 virtualEvent - string, name of the virtual event to test for, without
547 the enclosing '<< >>'
549 return ('<<'+virtualEvent+'>>') in self.GetCoreKeys().keys()
551 def GetCoreKeys(self, keySetName=None):
553 returns the requested set of core keybindings, with fallbacks if
554 required.
555 Keybindings loaded from the config file(s) are loaded _over_ these
556 defaults, so if there is a problem getting any core binding there will
557 be an 'ultimate last resort fallback' to the CUA-ish bindings
558 defined here.
560 keyBindings={
561 '<<copy>>': ['<Control-c>', '<Control-C>'],
562 '<<cut>>': ['<Control-x>', '<Control-X>'],
563 '<<paste>>': ['<Control-v>', '<Control-V>'],
564 '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
565 '<<center-insert>>': ['<Control-l>'],
566 '<<close-all-windows>>': ['<Control-q>'],
567 '<<close-window>>': ['<Alt-F4>'],
568 '<<do-nothing>>': ['<Control-x>'],
569 '<<end-of-file>>': ['<Control-d>'],
570 '<<python-docs>>': ['<F1>'],
571 '<<python-context-help>>': ['<Shift-F1>'],
572 '<<history-next>>': ['<Alt-n>'],
573 '<<history-previous>>': ['<Alt-p>'],
574 '<<interrupt-execution>>': ['<Control-c>'],
575 '<<view-restart>>': ['<F6>'],
576 '<<restart-shell>>': ['<Control-F6>'],
577 '<<open-class-browser>>': ['<Alt-c>'],
578 '<<open-module>>': ['<Alt-m>'],
579 '<<open-new-window>>': ['<Control-n>'],
580 '<<open-window-from-file>>': ['<Control-o>'],
581 '<<plain-newline-and-indent>>': ['<Control-j>'],
582 '<<print-window>>': ['<Control-p>'],
583 '<<redo>>': ['<Control-y>'],
584 '<<remove-selection>>': ['<Escape>'],
585 '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
586 '<<save-window-as-file>>': ['<Alt-s>'],
587 '<<save-window>>': ['<Control-s>'],
588 '<<select-all>>': ['<Alt-a>'],
589 '<<toggle-auto-coloring>>': ['<Control-slash>'],
590 '<<undo>>': ['<Control-z>'],
591 '<<find-again>>': ['<Control-g>', '<F3>'],
592 '<<find-in-files>>': ['<Alt-F3>'],
593 '<<find-selection>>': ['<Control-F3>'],
594 '<<find>>': ['<Control-f>'],
595 '<<replace>>': ['<Control-h>'],
596 '<<goto-line>>': ['<Alt-g>'],
597 '<<smart-backspace>>': ['<Key-BackSpace>'],
598 '<<newline-and-indent>>': ['<Key-Return> <Key-KP_Enter>'],
599 '<<smart-indent>>': ['<Key-Tab>'],
600 '<<indent-region>>': ['<Control-Key-bracketright>'],
601 '<<dedent-region>>': ['<Control-Key-bracketleft>'],
602 '<<comment-region>>': ['<Alt-Key-3>'],
603 '<<uncomment-region>>': ['<Alt-Key-4>'],
604 '<<tabify-region>>': ['<Alt-Key-5>'],
605 '<<untabify-region>>': ['<Alt-Key-6>'],
606 '<<toggle-tabs>>': ['<Alt-Key-t>'],
607 '<<change-indentwidth>>': ['<Alt-Key-u>'],
608 '<<del-word-left>>': ['<Control-Key-BackSpace>'],
609 '<<del-word-right>>': ['<Control-Key-Delete>']
611 if keySetName:
612 for event in keyBindings.keys():
613 binding=self.GetKeyBinding(keySetName,event)
614 if binding:
615 keyBindings[event]=binding
616 else: #we are going to return a default, print warning
617 warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
618 ' -\n problem retrieving key binding for event %r'
619 '\n from key set %r.\n'
620 ' returning default value: %r\n' %
621 (event, keySetName, keyBindings[event]))
622 try:
623 sys.stderr.write(warning)
624 except IOError:
625 pass
626 return keyBindings
628 def GetExtraHelpSourceList(self,configSet):
629 """Fetch list of extra help sources from a given configSet.
631 Valid configSets are 'user' or 'default'. Return a list of tuples of
632 the form (menu_item , path_to_help_file , option), or return the empty
633 list. 'option' is the sequence number of the help resource. 'option'
634 values determine the position of the menu items on the Help menu,
635 therefore the returned list must be sorted by 'option'.
638 helpSources=[]
639 if configSet=='user':
640 cfgParser=self.userCfg['main']
641 elif configSet=='default':
642 cfgParser=self.defaultCfg['main']
643 else:
644 raise InvalidConfigSet, 'Invalid configSet specified'
645 options=cfgParser.GetOptionList('HelpFiles')
646 for option in options:
647 value=cfgParser.Get('HelpFiles',option,default=';')
648 if value.find(';')==-1: #malformed config entry with no ';'
649 menuItem='' #make these empty
650 helpPath='' #so value won't be added to list
651 else: #config entry contains ';' as expected
652 value=string.split(value,';')
653 menuItem=value[0].strip()
654 helpPath=value[1].strip()
655 if menuItem and helpPath: #neither are empty strings
656 helpSources.append( (menuItem,helpPath,option) )
657 helpSources.sort(self.__helpsort)
658 return helpSources
660 def __helpsort(self, h1, h2):
661 if int(h1[2]) < int(h2[2]):
662 return -1
663 elif int(h1[2]) > int(h2[2]):
664 return 1
665 else:
666 return 0
668 def GetAllExtraHelpSourcesList(self):
670 Returns a list of tuples containing the details of all additional help
671 sources configured, or an empty list if there are none. Tuples are of
672 the format returned by GetExtraHelpSourceList.
674 allHelpSources=( self.GetExtraHelpSourceList('default')+
675 self.GetExtraHelpSourceList('user') )
676 return allHelpSources
678 def LoadCfgFiles(self):
680 load all configuration files.
682 for key in self.defaultCfg.keys():
683 self.defaultCfg[key].Load()
684 self.userCfg[key].Load() #same keys
686 def SaveUserCfgFiles(self):
688 write all loaded user configuration files back to disk
690 for key in self.userCfg.keys():
691 self.userCfg[key].Save()
693 idleConf=IdleConf()
695 ### module test
696 if __name__ == '__main__':
697 def dumpCfg(cfg):
698 print '\n',cfg,'\n'
699 for key in cfg.keys():
700 sections=cfg[key].sections()
701 print key
702 print sections
703 for section in sections:
704 options=cfg[key].options(section)
705 print section
706 print options
707 for option in options:
708 print option, '=', cfg[key].Get(section,option)
709 dumpCfg(idleConf.defaultCfg)
710 dumpCfg(idleConf.userCfg)
711 print idleConf.userCfg['main'].Get('Theme','name')
712 #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal')