@@ -44,7 +44,7 @@ def __init__(self, cfgFile, cfgDefaults=None):
4444 """
4545 cfgFile - string, fully specified configuration file name
4646 """
47- self .file = cfgFile
47+ self .file = cfgFile # This is currently '' when testing.
4848 ConfigParser .__init__ (self , defaults = cfgDefaults , strict = False )
4949
5050 def Get (self , section , option , type = None , default = None , raw = False ):
@@ -73,7 +73,8 @@ def GetOptionList(self, section):
7373
7474 def Load (self ):
7575 "Load the configuration file from disk."
76- self .read (self .file )
76+ if self .file :
77+ self .read (self .file )
7778
7879class IdleUserConfParser (IdleConfParser ):
7980 """
@@ -130,21 +131,22 @@ def RemoveFile(self):
130131 def Save (self ):
131132 """Update user configuration file.
132133
133- Remove empty sections. If resulting config isn't empty, write the file
134- to disk. If config is empty , remove the file from disk if it exists.
134+ If self not empty after removing empty sections , write the file
135+ to disk. Otherwise , remove the file from disk if it exists.
135136
136137 """
137- if not self .IsEmpty ():
138- fname = self .file
139- try :
140- cfgFile = open (fname , 'w' )
141- except OSError :
142- os .unlink (fname )
143- cfgFile = open (fname , 'w' )
144- with cfgFile :
145- self .write (cfgFile )
146- else :
147- self .RemoveFile ()
138+ fname = self .file
139+ if fname :
140+ if not self .IsEmpty ():
141+ try :
142+ cfgFile = open (fname , 'w' )
143+ except OSError :
144+ os .unlink (fname )
145+ cfgFile = open (fname , 'w' )
146+ with cfgFile :
147+ self .write (cfgFile )
148+ else :
149+ self .RemoveFile ()
148150
149151class IdleConf :
150152 """Hold config parsers for all idle config files in singleton instance.
@@ -158,7 +160,7 @@ class IdleConf:
158160 (user home dir)/.idlerc/config-{config-type}.cfg
159161 """
160162 def __init__ (self ):
161- self .config_types = ('main' , 'extensions ' , 'highlight ' , 'keys ' )
163+ self .config_types = ('main' , 'highlight ' , 'keys ' , 'extensions ' )
162164 self .defaultCfg = {}
163165 self .userCfg = {}
164166 self .cfg = {} # TODO use to select userCfg vs defaultCfg
@@ -766,7 +768,6 @@ def SaveUserCfgFiles(self):
766768
767769idleConf = IdleConf ()
768770
769-
770771_warned = set ()
771772def _warn (msg , * key ):
772773 key = (msg ,) + key
@@ -778,9 +779,100 @@ def _warn(msg, *key):
778779 _warned .add (key )
779780
780781
782+ class ConfigChanges (dict ):
783+ """Manage a user's proposed configuration option changes.
784+
785+ Names used across multiple methods:
786+ page -- one of the 4 top-level dicts representing a
787+ .idlerc/config-x.cfg file.
788+ config_type -- name of a page.
789+ section -- a section within a page/file.
790+ option -- name of an option within a section.
791+ value -- value for the option.
792+
793+ Methods
794+ add_option: Add option and value to changes.
795+ save_option: Save option and value to config parser.
796+ save_all: Save all the changes to the config parser and file.
797+ delete_section: Delete section if it exists.
798+ clear: Clear all changes by clearing each page.
799+ """
800+ def __init__ (self ):
801+ "Create a page for each configuration file"
802+ self .pages = [] # List of unhashable dicts.
803+ for config_type in idleConf .config_types :
804+ self [config_type ] = {}
805+ self .pages .append (self [config_type ])
806+
807+ def add_option (self , config_type , section , item , value ):
808+ "Add item/value pair for config_type and section."
809+ page = self [config_type ]
810+ value = str (value ) # Make sure we use a string.
811+ if section not in page :
812+ page [section ] = {}
813+ page [section ][item ] = value
814+
815+ @staticmethod
816+ def save_option (config_type , section , item , value ):
817+ """Return True if the configuration value was added or changed.
818+
819+ Helper for save_all.
820+ """
821+ if idleConf .defaultCfg [config_type ].has_option (section , item ):
822+ if idleConf .defaultCfg [config_type ].Get (section , item ) == value :
823+ # The setting equals a default setting, remove it from user cfg.
824+ return idleConf .userCfg [config_type ].RemoveOption (section , item )
825+ # If we got here, set the option.
826+ return idleConf .userCfg [config_type ].SetOption (section , item , value )
827+
828+ def save_all (self ):
829+ """Save configuration changes to the user config file.
830+
831+ Then clear self in preparation for additional changes.
832+ """
833+ idleConf .userCfg ['main' ].Save ()
834+ for config_type in self :
835+ cfg_type_changed = False
836+ page = self [config_type ]
837+ for section in page :
838+ if section == 'HelpFiles' : # Remove it for replacement.
839+ idleConf .userCfg ['main' ].remove_section ('HelpFiles' )
840+ cfg_type_changed = True
841+ for item , value in page [section ].items ():
842+ if self .save_option (config_type , section , item , value ):
843+ cfg_type_changed = True
844+ if cfg_type_changed :
845+ idleConf .userCfg [config_type ].Save ()
846+ for config_type in ['keys' , 'highlight' ]:
847+ # Save these even if unchanged!
848+ idleConf .userCfg [config_type ].Save ()
849+ self .clear ()
850+ # ConfigDialog caller must add the following call
851+ # self.save_all_changed_extensions() # Uses a different mechanism.
852+
853+ def delete_section (self , config_type , section ):
854+ """Delete a section from self, userCfg, and file.
855+
856+ Used to delete custom themes and keysets.
857+ """
858+ if section in self [config_type ]:
859+ del self [config_type ][section ]
860+ configpage = idleConf .userCfg [config_type ]
861+ configpage .remove_section (section )
862+ configpage .Save ()
863+
864+ def clear (self ):
865+ """Clear all 4 pages.
866+
867+ Called in save_all after saving to idleConf.
868+ XXX Mark window *title* when there are changes; unmark here.
869+ """
870+ for page in self .pages :
871+ page .clear ()
872+
873+
781874# TODO Revise test output, write expanded unittest
782- #
783- if __name__ == '__main__' :
875+ def _dump (): # htest # (not really, but ignore in coverage)
784876 from zlib import crc32
785877 line , crc = 0 , 0
786878
@@ -790,10 +882,10 @@ def sprint(obj):
790882 line += 1
791883 crc = crc32 (txt .encode (encoding = 'utf-8' ), crc )
792884 print (txt )
793- #print('***', line, crc, '***') # uncomment for diagnosis
885+ #print('***', line, crc, '***') # Uncomment for diagnosis.
794886
795887 def dumpCfg (cfg ):
796- print ('\n ' , cfg , '\n ' ) # has variable '0xnnnnnnnn' addresses
888+ print ('\n ' , cfg , '\n ' ) # Cfg has variable '0xnnnnnnnn' address.
797889 for key in sorted (cfg .keys ()):
798890 sections = cfg [key ].sections ()
799891 sprint (key )
@@ -808,3 +900,9 @@ def dumpCfg(cfg):
808900 dumpCfg (idleConf .defaultCfg )
809901 dumpCfg (idleConf .userCfg )
810902 print ('\n lines = ' , line , ', crc = ' , crc , sep = '' )
903+
904+ if __name__ == '__main__' :
905+ import unittest
906+ unittest .main ('idlelib.idle_test.test_config' ,
907+ verbosity = 2 , exit = False )
908+ #_dump()
0 commit comments