summaryrefslogtreecommitdiffstats
path: root/Lib/idlelib/config.py
diff options
context:
space:
mode:
authorterryjreedy <tjreedy@udel.edu>2017-07-07 20:00:57 (GMT)
committerGitHub <noreply@github.com>2017-07-07 20:00:57 (GMT)
commit349abd9e37dfdc077bc21f19e6ed2292c767f0e8 (patch)
treee06b11bc6d966506180914fbd928463867203265 /Lib/idlelib/config.py
parent1881befb905553618f1e7ad2cef8f6ff07e1b8ef (diff)
downloadcpython-349abd9e37dfdc077bc21f19e6ed2292c767f0e8.zip
cpython-349abd9e37dfdc077bc21f19e6ed2292c767f0e8.tar.gz
cpython-349abd9e37dfdc077bc21f19e6ed2292c767f0e8.tar.bz2
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.
Diffstat (limited to 'Lib/idlelib/config.py')
-rw-r--r--Lib/idlelib/config.py140
1 files changed, 119 insertions, 21 deletions
diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py
index 6d683e2..621e0bf 100644
--- a/Lib/idlelib/config.py
+++ b/Lib/idlelib/config.py
@@ -44,7 +44,7 @@ class IdleConfParser(ConfigParser):
"""
cfgFile - string, fully specified configuration file name
"""
- self.file = cfgFile
+ self.file = cfgFile # This is currently '' when testing.
ConfigParser.__init__(self, defaults=cfgDefaults, strict=False)
def Get(self, section, option, type=None, default=None, raw=False):
@@ -73,7 +73,8 @@ class IdleConfParser(ConfigParser):
def Load(self):
"Load the configuration file from disk."
- self.read(self.file)
+ if self.file:
+ self.read(self.file)
class IdleUserConfParser(IdleConfParser):
"""
@@ -130,21 +131,22 @@ class IdleUserConfParser(IdleConfParser):
def Save(self):
"""Update user configuration file.
- Remove empty sections. If resulting config isn't empty, write the file
- to disk. If config is empty, remove the file from disk if it exists.
+ If self not empty after removing empty sections, write the file
+ to disk. Otherwise, remove the file from disk if it exists.
"""
- if not self.IsEmpty():
- fname = self.file
- try:
- cfgFile = open(fname, 'w')
- except OSError:
- os.unlink(fname)
- cfgFile = open(fname, 'w')
- with cfgFile:
- self.write(cfgFile)
- else:
- self.RemoveFile()
+ fname = self.file
+ if fname:
+ if not self.IsEmpty():
+ try:
+ cfgFile = open(fname, 'w')
+ except OSError:
+ os.unlink(fname)
+ cfgFile = open(fname, 'w')
+ with cfgFile:
+ self.write(cfgFile)
+ else:
+ self.RemoveFile()
class IdleConf:
"""Hold config parsers for all idle config files in singleton instance.
@@ -158,7 +160,7 @@ class IdleConf:
(user home dir)/.idlerc/config-{config-type}.cfg
"""
def __init__(self):
- self.config_types = ('main', 'extensions', 'highlight', 'keys')
+ self.config_types = ('main', 'highlight', 'keys', 'extensions')
self.defaultCfg = {}
self.userCfg = {}
self.cfg = {} # TODO use to select userCfg vs defaultCfg
@@ -766,7 +768,6 @@ class IdleConf:
idleConf = IdleConf()
-
_warned = set()
def _warn(msg, *key):
key = (msg,) + key
@@ -778,9 +779,100 @@ def _warn(msg, *key):
_warned.add(key)
+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.
+ option -- name of an option within a section.
+ value -- value for the option.
+
+ Methods
+ add_option: Add option and value to changes.
+ save_option: Save option and value to config parser.
+ save_all: Save all the changes to the config parser and file.
+ delete_section: Delete section if it exists.
+ clear: Clear all changes by clearing each page.
+ """
+ def __init__(self):
+ "Create a page for each configuration file"
+ self.pages = [] # List of unhashable dicts.
+ for config_type in idleConf.config_types:
+ self[config_type] = {}
+ self.pages.append(self[config_type])
+
+ def add_option(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
+
+ @staticmethod
+ def save_option(config_type, section, item, value):
+ """Return True if the configuration value was added or changed.
+
+ Helper for save_all.
+ """
+ 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(self):
+ """Save configuration changes to the user config file.
+
+ Then clear self in preparation for additional changes.
+ """
+ idleConf.userCfg['main'].Save()
+ for config_type in self:
+ cfg_type_changed = False
+ page = self[config_type]
+ for section in page:
+ if section == 'HelpFiles': # Remove it for replacement.
+ idleConf.userCfg['main'].remove_section('HelpFiles')
+ cfg_type_changed = True
+ for item, value in page[section].items():
+ if self.save_option(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()
+ # ConfigDialog caller must add the following call
+ # self.save_all_changed_extensions() # Uses a different mechanism.
+
+ def delete_section(self, config_type, section):
+ """Delete a section from self, userCfg, and file.
+
+ Used to delete custom themes and keysets.
+ """
+ if section in self[config_type]:
+ del self[config_type][section]
+ configpage = idleConf.userCfg[config_type]
+ configpage.remove_section(section)
+ configpage.Save()
+
+ def clear(self):
+ """Clear all 4 pages.
+
+ Called in save_all after saving to idleConf.
+ XXX Mark window *title* when there are changes; unmark here.
+ """
+ for page in self.pages:
+ page.clear()
+
+
# TODO Revise test output, write expanded unittest
-#
-if __name__ == '__main__':
+def _dump(): # htest # (not really, but ignore in coverage)
from zlib import crc32
line, crc = 0, 0
@@ -790,10 +882,10 @@ if __name__ == '__main__':
line += 1
crc = crc32(txt.encode(encoding='utf-8'), crc)
print(txt)
- #print('***', line, crc, '***') # uncomment for diagnosis
+ #print('***', line, crc, '***') # Uncomment for diagnosis.
def dumpCfg(cfg):
- print('\n', cfg, '\n') # has variable '0xnnnnnnnn' addresses
+ print('\n', cfg, '\n') # Cfg has variable '0xnnnnnnnn' address.
for key in sorted(cfg.keys()):
sections = cfg[key].sections()
sprint(key)
@@ -808,3 +900,9 @@ if __name__ == '__main__':
dumpCfg(idleConf.defaultCfg)
dumpCfg(idleConf.userCfg)
print('\nlines = ', line, ', crc = ', crc, sep='')
+
+if __name__ == '__main__':
+ import unittest
+ unittest.main('idlelib.idle_test.test_config',
+ verbosity=2, exit=False)
+ #_dump()