FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

gh-66172: Don't let a corrupt config file prevent IDLE from starting by serhiy-storchaka · Pull Request #152764 · python/cpython · GitHub

Repository navigation

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .py  (3) .rst  (1) All 2 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
28 changes: 25 additions & 3 deletions Lib/idlelib/config.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"""
# TODOs added Oct 2014, tjr

from configparser import ConfigParser
from configparser import ConfigParser, Error as ConfigParserError
import os
import sys

Expand Down Expand Up @@ -74,7 +74,7 @@ def GetOptionList(self, section):
def Load(self):
"Load the configuration file from disk."
if self.file and os.path.exists(self.file):
with open(self.file, encoding='utf-8', errors='replace') as f:
with open(self.file, encoding='utf-8') as f:
self.read_file(f)

class IdleUserConfParser(IdleConfParser):
Expand Down Expand Up @@ -159,6 +159,7 @@ def __init__(self, _utest=False):
self.defaultCfg = {}
self.userCfg = {}
self.cfg = {} # TODO use to select userCfg vs defaultCfg
self.file_load_errors = [] # (file, error) for unparsable cfg files.

# See https://bugs.python.org/issue4630#msg356516 for following.
# self.blink_off_time = <first editor text>['insertofftime']
Expand Down Expand Up @@ -795,7 +796,28 @@ def LoadCfgFiles(self):
"Load all configuration files."
for key in self.defaultCfg:
self.defaultCfg[key].Load()
self.userCfg[key].Load() #same keys
try:
self.userCfg[key].Load() # same keys
except (ConfigParserError, UnicodeDecodeError) as err:
# Move an invalid user file aside instead of losing it
# or failing to start (gh-66172).
file = self.userCfg[key].file
self.file_load_errors.append((file, err))
try:
os.replace(file, file + '.bad')
except OSError:
pass

def file_load_error_message(self):
"Return a warning about invalid config files, or None."
if not self.file_load_errors:
return None
files = '\n'.join(
f' {file}:\n {type(err).__name__}: {str(err).splitlines()[0]}'
for file, err in self.file_load_errors)
return ('The following IDLE configuration files could not be read. '
'They were renamed by appending ".bad", and default settings '
'are used instead:\n\n' + files)

def SaveUserCfgFiles(self):
"Write all loaded user configuration files to disk."
Expand Down
43 changes: 43 additions & 0 deletions Lib/idlelib/idle_test/test_config.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,49 @@ def test_load_cfg_files(self):
eq(conf.userCfg['foo'].Get('Foo Bar', 'foo'), 'newbar')
eq(conf.userCfg['foo'].GetOptionList('Foo Bar'), ['foo'])

def test_load_cfg_files_bad_format(self):
# gh-66172: rename an unparsable user file and save the exception.
conf = self.new_config(_utest=True)
tmpdir = tempfile.TemporaryDirectory()
self.addCleanup(tmpdir.cleanup)
confpath = os.path.join(tmpdir.name, 'config-extensions.cfg')
with open(confpath, 'w') as f:
f.write('enable=1\n') # No section header.
conf.defaultCfg['foo'] = config.IdleConfParser('') # Empty, valid.
conf.userCfg['foo'] = config.IdleUserConfParser(confpath)

self.assertIsNone(conf.file_load_error_message())
conf.LoadCfgFiles() # Must not raise.

self.assertEqual(len(conf.file_load_errors), 1)
file, err = conf.file_load_errors[0]
self.assertEqual(file, confpath)
# The bad file is moved aside, not left to be overwritten or deleted.
self.assertFalse(os.path.exists(confpath))
with open(confpath + '.bad') as f:
self.assertEqual(f.read(), 'enable=1\n')
message = conf.file_load_error_message()
self.assertIn(confpath, message)
self.assertIn('MissingSectionHeaderError', message)

def test_load_cfg_files_bad_encoding(self):
# gh-66172: a file that is not valid UTF-8 is handled like a bad parse.
conf = self.new_config(_utest=True)
tmpdir = tempfile.TemporaryDirectory()
self.addCleanup(tmpdir.cleanup)
confpath = os.path.join(tmpdir.name, 'config-main.cfg')
with open(confpath, 'wb') as f:
f.write(b'[Section]\nkey = \xff\n') # Invalid UTF-8.
conf.defaultCfg['foo'] = config.IdleConfParser('') # Empty, valid.
conf.userCfg['foo'] = config.IdleUserConfParser(confpath)

conf.LoadCfgFiles() # Must not raise.

self.assertEqual(len(conf.file_load_errors), 1)
self.assertIsInstance(conf.file_load_errors[0][1], UnicodeDecodeError)
self.assertFalse(os.path.exists(confpath))
self.assertTrue(os.path.exists(confpath + '.bad'))

def test_save_user_cfg_files(self):
conf = self.mock_config()

Expand Down
6 changes: 6 additions & 0 deletions Lib/idlelib/pyshell.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -1610,6 +1610,12 @@ def main():
from idlelib.run import fix_scaling
fix_scaling(root)

# Warn about configuration files that could not be parsed (gh-66172).
config_error = idleConf.file_load_error_message()
if config_error:
messagebox.showwarning('IDLE Configuration Warning', config_error,
parent=root)
Comment thread
terryjreedy marked this conversation as resolved.

# set application icon
icondir = os.path.join(os.path.dirname(__file__), 'Icons')
if system() == 'Windows':
Expand Down
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
IDLE no longer fails to start when a user configuration file is corrupt.
The unparsable file is renamed with a ".bad" suffix, default settings are
used instead, and a warning lists the affected files.
Loading

Back | FazBrowse Home | New Git URL