bpo-38183: Test_idle ignores user config directory GH-16198)

It no longer tries to create or access .idlerc or any files within.
Users must run IDLE to discover problems with saving settings.
(cherry picked from commit 0048afc16a)

Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
This commit is contained in:
Miss Islington (bot) 2019-09-16 20:32:55 -07:00 committed by GitHub
parent 7076764992
commit ad845becf2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 45 additions and 35 deletions

View file

@ -130,7 +130,7 @@ class IdleUserConfParser(IdleConfParser):
to disk. Otherwise, remove the file from disk if it exists. to disk. Otherwise, remove the file from disk if it exists.
""" """
fname = self.file fname = self.file
if fname: if fname and fname[0] != '#':
if not self.IsEmpty(): if not self.IsEmpty():
try: try:
cfgFile = open(fname, 'w') cfgFile = open(fname, 'w')
@ -166,12 +166,12 @@ class IdleConf:
def CreateConfigHandlers(self): def CreateConfigHandlers(self):
"Populate default and user config parser dictionaries." "Populate default and user config parser dictionaries."
idledir = os.path.dirname(__file__) idledir = os.path.dirname(__file__)
self.userdir = userdir = self.GetUserCfgDir() self.userdir = userdir = '' if idlelib.testing else self.GetUserCfgDir()
for cfg_type in self.config_types: for cfg_type in self.config_types:
self.defaultCfg[cfg_type] = IdleConfParser( self.defaultCfg[cfg_type] = IdleConfParser(
os.path.join(idledir, f'config-{cfg_type}.def')) os.path.join(idledir, f'config-{cfg_type}.def'))
self.userCfg[cfg_type] = IdleUserConfParser( self.userCfg[cfg_type] = IdleUserConfParser(
os.path.join(userdir, f'config-{cfg_type}.cfg')) os.path.join(userdir or '#', f'config-{cfg_type}.cfg'))
def GetUserCfgDir(self): def GetUserCfgDir(self):
"""Return a filesystem directory for storing user config files. """Return a filesystem directory for storing user config files.
@ -182,12 +182,13 @@ class IdleConf:
userDir = os.path.expanduser('~') userDir = os.path.expanduser('~')
if userDir != '~': # expanduser() found user home dir if userDir != '~': # expanduser() found user home dir
if not os.path.exists(userDir): if not os.path.exists(userDir):
warn = ('\n Warning: os.path.expanduser("~") points to\n ' + if not idlelib.testing:
userDir + ',\n but the path does not exist.') warn = ('\n Warning: os.path.expanduser("~") points to\n ' +
try: userDir + ',\n but the path does not exist.')
print(warn, file=sys.stderr) try:
except OSError: print(warn, file=sys.stderr)
pass except OSError:
pass
userDir = '~' userDir = '~'
if userDir == "~": # still no path to home! if userDir == "~": # still no path to home!
# traditionally IDLE has defaulted to os.getcwd(), is this adequate? # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
@ -197,10 +198,13 @@ class IdleConf:
try: try:
os.mkdir(userDir) os.mkdir(userDir)
except OSError: except OSError:
warn = ('\n Warning: unable to create user config directory\n' +
userDir + '\n Check path and permissions.\n Exiting!\n')
if not idlelib.testing: if not idlelib.testing:
print(warn, file=sys.stderr) warn = ('\n Warning: unable to create user config directory\n' +
userDir + '\n Check path and permissions.\n Exiting!\n')
try:
print(warn, file=sys.stderr)
except OSError:
pass
raise SystemExit raise SystemExit
# TODO continue without userDIr instead of exit # TODO continue without userDIr instead of exit
return userDir return userDir

View file

@ -116,7 +116,7 @@ class EditorWindow(object):
self.tkinter_vars = {} # keys: Tkinter event names self.tkinter_vars = {} # keys: Tkinter event names
# values: Tkinter variable instances # values: Tkinter variable instances
self.top.instance_dict = {} self.top.instance_dict = {}
self.recent_files_path = os.path.join( self.recent_files_path = idleConf.userdir and os.path.join(
idleConf.userdir, 'recent-files.lst') idleConf.userdir, 'recent-files.lst')
self.prompt_last_line = '' # Override in PyShell self.prompt_last_line = '' # Override in PyShell
@ -924,9 +924,11 @@ class EditorWindow(object):
def update_recent_files_list(self, new_file=None): def update_recent_files_list(self, new_file=None):
"Load and update the recent files list and menus" "Load and update the recent files list and menus"
# TODO: move to iomenu.
rf_list = [] rf_list = []
if os.path.exists(self.recent_files_path): file_path = self.recent_files_path
with open(self.recent_files_path, 'r', if file_path and os.path.exists(file_path):
with open(file_path, 'r',
encoding='utf_8', errors='replace') as rf_list_file: encoding='utf_8', errors='replace') as rf_list_file:
rf_list = rf_list_file.readlines() rf_list = rf_list_file.readlines()
if new_file: if new_file:
@ -942,19 +944,19 @@ class EditorWindow(object):
rf_list = [path for path in rf_list if path not in bad_paths] rf_list = [path for path in rf_list if path not in bad_paths]
ulchars = "1234567890ABCDEFGHIJK" ulchars = "1234567890ABCDEFGHIJK"
rf_list = rf_list[0:len(ulchars)] rf_list = rf_list[0:len(ulchars)]
try: if file_path:
with open(self.recent_files_path, 'w', try:
encoding='utf_8', errors='replace') as rf_file: with open(file_path, 'w',
rf_file.writelines(rf_list) encoding='utf_8', errors='replace') as rf_file:
except OSError as err: rf_file.writelines(rf_list)
if not getattr(self.root, "recentfilelist_error_displayed", False): except OSError as err:
self.root.recentfilelist_error_displayed = True if not getattr(self.root, "recentfiles_message", False):
tkMessageBox.showwarning(title='IDLE Warning', self.root.recentfiles_message = True
message="Cannot update File menu Recent Files list. " tkMessageBox.showwarning(title='IDLE Warning',
"Your operating system says:\n%s\n" message="Cannot save Recent Files list to disk.\n"
"Select OK and IDLE will continue without updating." f" {err}\n"
% self._filename_to_unicode(str(err)), "Select OK to continue.",
parent=self.text) parent=self.text)
# for each edit window instance, construct the recent files menu # for each edit window instance, construct the recent files menu
for instance in self.top.instance_dict: for instance in self.top.instance_dict:
menu = instance.recent_files_menu menu = instance.recent_files_menu

View file

@ -220,7 +220,7 @@ class IdleConfTest(unittest.TestCase):
@unittest.skipIf(sys.platform.startswith('win'), 'this is test for unix system') @unittest.skipIf(sys.platform.startswith('win'), 'this is test for unix system')
def test_get_user_cfg_dir_unix(self): def test_get_user_cfg_dir_unix(self):
"Test to get user config directory under unix" # Test to get user config directory under unix.
conf = self.new_config(_utest=True) conf = self.new_config(_utest=True)
# Check normal way should success # Check normal way should success
@ -243,7 +243,7 @@ class IdleConfTest(unittest.TestCase):
@unittest.skipIf(not sys.platform.startswith('win'), 'this is test for Windows system') @unittest.skipIf(not sys.platform.startswith('win'), 'this is test for Windows system')
def test_get_user_cfg_dir_windows(self): def test_get_user_cfg_dir_windows(self):
"Test to get user config directory under Windows" # Test to get user config directory under Windows.
conf = self.new_config(_utest=True) conf = self.new_config(_utest=True)
# Check normal way should success # Check normal way should success
@ -284,12 +284,12 @@ class IdleConfTest(unittest.TestCase):
self.assertIsInstance(user_parser, config.IdleUserConfParser) self.assertIsInstance(user_parser, config.IdleUserConfParser)
# Check config path are correct # Check config path are correct
for config_type, parser in conf.defaultCfg.items(): for cfg_type, parser in conf.defaultCfg.items():
self.assertEqual(parser.file, self.assertEqual(parser.file,
os.path.join(idle_dir, 'config-%s.def' % config_type)) os.path.join(idle_dir, f'config-{cfg_type}.def'))
for config_type, parser in conf.userCfg.items(): for cfg_type, parser in conf.userCfg.items():
self.assertEqual(parser.file, self.assertEqual(parser.file,
os.path.join(conf.userdir, 'config-%s.cfg' % config_type)) os.path.join(conf.userdir or '#', f'config-{cfg_type}.cfg'))
def test_load_cfg_files(self): def test_load_cfg_files(self):
conf = self.new_config(_utest=True) conf = self.new_config(_utest=True)
@ -373,7 +373,7 @@ class IdleConfTest(unittest.TestCase):
'background': '#171717'}) 'background': '#171717'})
def test_get_theme_dict(self): def test_get_theme_dict(self):
"XXX: NOT YET DONE" # TODO: finish.
conf = self.mock_config() conf = self.mock_config()
# These two should be the same # These two should be the same

View file

@ -133,6 +133,7 @@ class PyShellEditorWindow(EditorWindow):
self.text.bind("<<clear-breakpoint-here>>", self.clear_breakpoint_here) self.text.bind("<<clear-breakpoint-here>>", self.clear_breakpoint_here)
self.text.bind("<<open-python-shell>>", self.flist.open_shell) self.text.bind("<<open-python-shell>>", self.flist.open_shell)
#TODO: don't read/write this from/to .idlerc when testing
self.breakpointPath = os.path.join( self.breakpointPath = os.path.join(
idleConf.userdir, 'breakpoints.lst') idleConf.userdir, 'breakpoints.lst')
# whenever a file is changed, restore breakpoints # whenever a file is changed, restore breakpoints

View file

@ -0,0 +1,3 @@
To avoid problems, test_idle ignores the user config directory.
It no longer tries to create or access .idlerc or any files within.
Users must run IDLE to discover problems with saving settings.